diff --git a/accessible/src/html/nsHyperTextAccessible.cpp b/accessible/src/html/nsHyperTextAccessible.cpp
index 1b0fce62ff22..c1060c317002 100644
--- a/accessible/src/html/nsHyperTextAccessible.cpp
+++ b/accessible/src/html/nsHyperTextAccessible.cpp
@@ -1631,6 +1631,9 @@ 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) &&
diff --git a/b2g/installer/package-manifest.in b/b2g/installer/package-manifest.in
index ff4274923561..5e3d84ae0f6b 100644
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -151,6 +151,9 @@
 @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
diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js
index 9caa62d572a0..f89a8abda58c 100644
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -357,6 +357,9 @@ pref("browser.search.order.3",                "chrome://browser-region/locale/re
 // 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);
 
diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js
index e59319f098ff..3adbc57d129c 100644
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -3555,10 +3555,11 @@ const BrowserSearch = {
     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 });
   },
 
diff --git a/browser/base/content/tabbrowser.xml b/browser/base/content/tabbrowser.xml
index 30f18e072758..90aa31dba5dd 100644
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -536,7 +536,8 @@
                 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)
@@ -3565,8 +3566,10 @@
 
         // 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[
@@ -3981,24 +3984,18 @@
 
     <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"
@@ -4046,10 +4043,6 @@
             return;
 
           switch (event.type) {
-            case "findbaropen":
-              this.setAttribute("mirror", "true");
-              this._calcMouseTargetRect();
-              break;
             case "resize":
               this._calcMouseTargetRect();
               break;
@@ -4059,7 +4052,7 @@
 
       <method name="_calcMouseTargetRect">
         <body><![CDATA[
-          let alignRight = (window.gFindBarInitialized && !window.gFindBar.hidden);
+          let alignRight = false;
 
           if (getComputedStyle(document.documentElement).direction == "rtl")
             alighRight = !alignRight;
diff --git a/browser/base/content/test/authenticate.sjs b/browser/base/content/test/authenticate.sjs
index 7c2102fd0dbb..58da655cf98f 100644
--- a/browser/base/content/test/authenticate.sjs
+++ b/browser/base/content/test/authenticate.sjs
@@ -15,24 +15,26 @@ function reallyHandleRequest(request, response) {
 
   // 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];
 
@@ -66,6 +68,10 @@ function reallyHandleRequest(request, response) {
   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.
   //
@@ -74,8 +80,9 @@ function reallyHandleRequest(request, response) {
   // 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)
@@ -115,16 +122,24 @@ function reallyHandleRequest(request, response) {
     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);
diff --git a/browser/base/content/test/browser_homeDrop.js b/browser/base/content/test/browser_homeDrop.js
index a6cb961ae211..c8c63cc1a7a6 100644
--- a/browser/base/content/test/browser_homeDrop.js
+++ b/browser/base/content/test/browser_homeDrop.js
@@ -28,14 +28,18 @@ function 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:
diff --git a/browser/devtools/debugger/test/browser_dbg_select-line.js b/browser/devtools/debugger/test/browser_dbg_select-line.js
index 57e7b8186b9e..885e5786c96a 100644
--- a/browser/devtools/debugger/test/browser_dbg_select-line.js
+++ b/browser/devtools/debugger/test/browser_dbg_select-line.js
@@ -43,37 +43,41 @@ function testSelectLine() {
       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);
   });
 
diff --git a/browser/installer/package-manifest.in b/browser/installer/package-manifest.in
index 21fc0c3088e6..98f687c2dac2 100644
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -150,6 +150,9 @@
 @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
@@ -398,6 +401,8 @@
 #endif
 @BINPATH@/components/TelemetryPing.js
 @BINPATH@/components/TelemetryPing.manifest
+@BINPATH@/components/messageWakeupService.js
+@BINPATH@/components/messageWakeupService.manifest
 
 ; Modules
 @BINPATH@/modules/*
diff --git a/browser/locales/en-US/searchplugins/google-params.inc b/browser/locales/en-US/searchplugins/google-params.inc
deleted file mode 100644
index 1764477c8f88..000000000000
--- 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
diff --git a/browser/locales/en-US/searchplugins/google.xml b/browser/locales/en-US/searchplugins/google.xml
index 3e6794c39e48..6bf3f44cca36 100644
--- a/browser/locales/en-US/searchplugins/google.xml
+++ b/browser/locales/en-US/searchplugins/google.xml
@@ -1,3 +1,13 @@
+#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>
@@ -5,16 +15,19 @@
 <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>
diff --git a/build/mobile/robocop/Actions.java.in b/build/mobile/robocop/Actions.java.in
index 7be67df3cf54..0c123016921d 100644
--- a/build/mobile/robocop/Actions.java.in
+++ b/build/mobile/robocop/Actions.java.in
@@ -41,43 +41,55 @@ package @ANDROID_PACKAGE_NAME@;
 import java.util.List;
 
 public interface Actions {
-  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();
-  }
+    /** Special keys supported by sendSpecialKey() */
+    public enum SpecialKey {
+        DOWN, UP, LEFT, RIGHT, ENTER, MENU, BACK
+    }
 
-  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);
-  }
+    public interface EventExpecter {
+        /** Blocks until the event has been received. Subsequent calls will return immediately. */
+        public void blockForEvent();
 
-  /**
-   * 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);
+        /** Polls to see if the event has been received. Once this returns true, subsequent calls will also return true. */
+        public boolean eventReceived();
+    }
 
-  /**
-   * 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();
+    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);
+    }
 
-  // Send the string kewsToSend to the application 
-  void sendKeys(String keysToSend);
-  //Send any of the above keys to the element
-  void sendSpecialKey(SpecialKey button);
+    /**
+     * 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);
 
-  void drag(int startingX, int endingX, int startingY, int endingY);
+    /**
+     * 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 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);
 }
diff --git a/build/mobile/robocop/Assert.java.in b/build/mobile/robocop/Assert.java.in
index d3afb7bd31c3..28a14dbdb5c9 100644
--- a/build/mobile/robocop/Assert.java.in
+++ b/build/mobile/robocop/Assert.java.in
@@ -40,19 +40,19 @@
 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);
 }
diff --git a/build/mobile/robocop/Driver.java.in b/build/mobile/robocop/Driver.java.in
index b026fe47a23c..e358ec89a239 100644
--- a/build/mobile/robocop/Driver.java.in
+++ b/build/mobile/robocop/Driver.java.in
@@ -43,39 +43,39 @@ 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();
 }
diff --git a/build/mobile/robocop/Element.java.in b/build/mobile/robocop/Element.java.in
index 76bbbd716730..2c87bd208f9e 100644
--- a/build/mobile/robocop/Element.java.in
+++ b/build/mobile/robocop/Element.java.in
@@ -39,13 +39,21 @@
 
 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();
 }
diff --git a/build/mobile/robocop/FennecMochitestAssert.java.in b/build/mobile/robocop/FennecMochitestAssert.java.in
index 35a596522427..20199ec51109 100644
--- a/build/mobile/robocop/FennecMochitestAssert.java.in
+++ b/build/mobile/robocop/FennecMochitestAssert.java.in
@@ -38,202 +38,194 @@
 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> mTestList = new LinkedList<testInfo>();
 
-  private LinkedList<testInfo> testList = new LinkedList<testInfo>();
+    // 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;
 
-  // 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 mLogTestName = "";
 
-  // Used to write the test-start/test-end log lines
-  private String logTestName = "";
+    // Measure the time it takes to run test case
+    private long mStartTime = 0;
 
-  // Measure the time it takes to run test case
-  private long startTime = 0;
-
-  public FennecMochitestAssert() {
-  }
-
-  // Write information to a logfile and logcat
-  public void dumpLog(String message)
-  {
-    FennecNativeDriver.log(FennecNativeDriver.LogLevel.LOG_LEVEL_INFO, message);
-  }
-
-  // Set the filename used for dumpLog.
-  public void setLogFile(String filename)
-  {
-    FennecNativeDriver.setLogFile(filename);
-
-    String message;
-    if (!logStarted) {
-      dumpLog(Integer.toString(lineNumber++) + " INFO SimpleTest START");
-      logStarted = true;
+    public FennecMochitestAssert() {
     }
 
-    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 setTestName(String testName)
-  {
-    String[] nameParts = testName.split("\\.");
-    logTestName = nameParts[nameParts.length - 1];
-    startTime = new Date().getTime();
-
-    dumpLog(Integer.toString(lineNumber++) + " INFO TEST-START | " + logTestName);
-  }
-
-  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;
+    /** Write information to a logfile and logcat */
+    public void dumpLog(String message) {
+        FennecNativeDriver.log(FennecNativeDriver.LogLevel.LOG_LEVEL_INFO, message);
     }
 
-  }
+    /** Set the filename used for dumpLog. */
+    public void setLogFile(String filename) {
+        FennecNativeDriver.setLogFile(filename);
 
-  private void _logMochitestResult(testInfo test, String passString, String failString)
-  {
-    boolean isError = true;
-    String resultString = failString;
-    if (test.result || test.todo) {
-      isError = false;
-    }
-    if (test.result)
-    {
-      resultString = passString;
-    }
-    String diag = test.name;
-    if (test.diag != null) diag += " - " + test.diag;
+        String message;
+        if (!mLogStarted) {
+            dumpLog(Integer.toString(mLineNumber++) + " INFO SimpleTest START");
+            mLogStarted = true;
+        }
 
-    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 (logTestName != "") {
-      long diff = (new Date().getTime()) - startTime;
-      message = Integer.toString(lineNumber++) + " INFO TEST-END | " + logTestName;
-      message += " | finished in " + diff + "ms";
-      dumpLog(message);
-      logTestName = "";
+        if (mLogTestName != "") {
+            long diff = SystemClock.uptimeMillis() - mStartTime;
+            message = Integer.toString(mLineNumber++) + " INFO TEST-END | " + mLogTestName;
+            message += " | finished in " + diff + "ms";
+            dumpLog(message);
+            mLogTestName = "";
+        }
     }
 
-    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 setTestName(String testName) {
+        String[] nameParts = testName.split("\\.");
+        mLogTestName = nameParts[nameParts.length - 1];
+        mStartTime = SystemClock.uptimeMillis();
 
-  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();
+        dumpLog(Integer.toString(mLineNumber++) + " INFO TEST-START | " + mLogTestName);
     }
-    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();
+
+    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;
+        }
+
     }
-    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 + ")");
-  }
+    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;
 
-  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);
-  }
+        String message = Integer.toString(mLineNumber++) + " INFO " + resultString + " | " + mLogTestName + " | " + diag;
+        dumpLog(message);
 
-  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();
+        if (test.mTodo) {
+            mTodo++;
+        } else if (isError) {
+            mFailed++;
+        } else {
+            mPassed++;
+        }
+        if (isError) {
+            junit.framework.Assert.fail(message);
+        }
     }
-    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();
-    }
-    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 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 = "";
+        }
+
+        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);
+    }
+
+    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);
+    }
+
+    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);
+    }
+    
+    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);
+    }
+
+    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");
+        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();
+        }
+        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();
+        }
+        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?");
+    }
 }
diff --git a/build/mobile/robocop/FennecNativeActions.java.in b/build/mobile/robocop/FennecNativeActions.java.in
index 1992d87c83e0..973a3fd70bb6 100644
--- a/build/mobile/robocop/FennecNativeActions.java.in
+++ b/build/mobile/robocop/FennecNativeActions.java.in
@@ -59,295 +59,294 @@ 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);
-
-      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;
-    }
-
-    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 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) {
+    public FennecNativeActions(Activity activity, Solo robocop, Instrumentation instrumentation) {
+        mSolo = robocop;
+        mInstr = instrumentation;
+        mGeckoApp = activity;
+        // Set up reflexive access of java classes and methods.
         try {
-          this.wait();
-        } catch (InterruptedException ie) {
-          ie.printStackTrace();
-          break;
+            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);
+
+            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();
+         }
+    }
+
+    class wakeInvocationHandler implements InvocationHandler {
+        private final GeckoEventExpecter mEventExpecter;
+
+        public wakeInvocationHandler(GeckoEventExpecter expecter) {
+            mEventExpecter = expecter;
+        }
+
+        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;
-    }
+    class GeckoEventExpecter implements EventExpecter {
+        private final String mGeckoEvent;
+        private final Object[] mRegistrationParams;
+        private boolean 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();
-      }
-    }
-  }
-  
-  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;
-  }
+        GeckoEventExpecter(String geckoEvent, Object[] registrationParams) {
+            mGeckoEvent = geckoEvent;
+            mRegistrationParams = registrationParams;
+        }
 
-  class DrawListenerProxy implements InvocationHandler {
-    private final PaintExpecter mPaintExpecter;
+        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);
+        }
 
-    DrawListenerProxy(PaintExpecter paintExpecter) {
-      mPaintExpecter = paintExpecter;
+        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 Object invoke(Object proxy, Method method, Object[] args) {
-      String methodName = method.getName();
-      if ("drawFinished".equals(methodName)) {
+    
+    public EventExpecter expectGeckoEvent(String geckoEvent) {
         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 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 synchronized void blockForEvent() {
-      while (! mPaintDone) {
+            "waiting for "+geckoEvent);
         try {
-          this.wait();
-        } catch (InterruptedException ie) {
-          ie.printStackTrace();
-          break;
+            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();
         }
-      }
-      try {
-        setDrawListener.invoke(mLayerClient, (Object)null);
-      } catch (Exception e) {
-        e.printStackTrace();
-      }
+        return null;
     }
 
-    public synchronized boolean eventReceived() {
-      return mPaintDone;
+    class DrawListenerProxy implements InvocationHandler {
+        private final PaintExpecter mPaintExpecter;
+
+        DrawListenerProxy(PaintExpecter paintExpecter) {
+            mPaintExpecter = paintExpecter;
+        }
+
+        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 blockUntilClear(long millis) {
-      if (millis <= 0) {
-        throw new IllegalArgumentException("millis must be > 0");
-      }
-      // wait for at least one event
-      while (! mPaintDone) {
+    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;
+        }
+
+        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 RepeatedEventExpecter expectPaint() {
         try {
-          this.wait();
-        } catch (InterruptedException ie) {
-          ie.printStackTrace();
-          break;
+            return new PaintExpecter();
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
         }
-      }
-      // 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;
+    }
+
+    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;
         }
-        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 {
-        setDrawListener.invoke(mLayerClient, (Object)null);
-      } catch (Exception e) {
-        e.printStackTrace();
-      }
     }
-  }
 
-  public RepeatedEventExpecter expectPaint() {
-    try {
-      return new PaintExpecter();
-    } catch (Exception e) {
-      e.printStackTrace();
-      return null;
+    @Override
+    public void sendKeys(String input) {
+        mInstr.sendStringSync(input);
     }
-  }
 
-  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;
+    public void drag(int startingX, int endingX, int startingY, int endingY) {
+        mSolo.drag(startingX, endingX, startingY, endingY, 10);
     }
-  }
-
-  @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);
-  }
 }
diff --git a/build/mobile/robocop/FennecNativeDriver.java.in b/build/mobile/robocop/FennecNativeDriver.java.in
index 76263ca7d782..eb6d048dcb54 100644
--- a/build/mobile/robocop/FennecNativeDriver.java.in
+++ b/build/mobile/robocop/FennecNativeDriver.java.in
@@ -46,8 +46,6 @@ 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;
 
@@ -68,396 +66,405 @@ 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 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;
+    // 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;
 
-  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;
-    }
-  }
-
-  public FennecNativeDriver(Activity activity, Solo robocop){
-    this.activity = activity;
-    this.solo = robocop;
-
-    // Set up table of fennec_ids.
-    locators = convertTextToTable(getFile("/mnt/sdcard/fennec_ids.txt"));
-
-    // 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 = 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 = 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 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");
-    }
-  }
-
-  public int getGeckoTop() {
-    if(!geckoInfo) {
-      getGeckoInfo();
-    }
-    return geckoTop;
-  }
-
-  public int getGeckoLeft() {
-    if(!geckoInfo) {
-      getGeckoInfo();
-    }
-    return geckoLeft;
-  }
-
-  public int getGeckoHeight() {
-    if(!geckoInfo) {
-      getGeckoInfo();
-    }
-    return geckoHeight;
-  }
-  public int getGeckoWidth() {
-    if(!geckoInfo) {
-      getGeckoInfo();
-    }
-    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);
-    }
-    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 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++;
+        private int mValue;
+        LogLevel(int value) {
+            mValue = value;
         }
-        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;
-
-    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;
-  }
-
-  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;
-    }
-
-    // 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 boolean isEnabled(LogLevel configuredLevel) {
+            return mValue >= configuredLevel.getValue();
+        }
+        private int getValue() {
+            return mValue;
         }
-
-      } catch( Throwable e) {
-        FennecNativeDriver.log(FennecNativeDriver.LogLevel.LOG_LEVEL_WARN, 
-          "WARNING: ScrollReceived, but read wrong!");
-      }
-      return null;
-    }
-  }
-  public int getScrollHeight() {
-    return scrollHeight;
-  }
-  public int getPageHeight() {
-    return pageHeight;
-  }
-  public int getHeight() {
-    return 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();
     }
 
-  }
+    public FennecNativeDriver(Activity activity, Solo robocop) {
+        mActivity = activity;
+        mSolo = robocop;
 
-  //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();
+        // Set up table of fennec_ids.
+        mLocators = convertTextToTable(getFile("/mnt/sdcard/fennec_ids.txt"));
 
-    BufferedReader br = null;
-    try {
-      br = new BufferedReader(new FileReader(filename));
-      String line;
-
-      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();  
-  }
-
-  // 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());
-    }
-    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();
-    }
-  }
-
-  public static void setLogLevel(LogLevel level) {
-    mLogLevel = level;
-  }
-
-  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 {
+        // Set up reflexive access of java classes and methods.
         try {
-          if (bw != null) {
-            bw.flush();
-            bw.close();
-          }
-        } catch (IOException ex) {
-          ex.printStackTrace();
-        }
-      }
+            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);
+
+            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 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();
+         }
     }
 
-    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);
+    //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;
+
+    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 (!mGeckoInfo) {
+            getGeckoInfo();
+        }
+        return mGeckoTop;
+    }
+
+    public int getGeckoLeft() {
+        if (!mGeckoInfo) {
+            getGeckoInfo();
+        }
+        return mGeckoLeft;
+    }
+
+    public int getGeckoHeight() {
+        if (!mGeckoInfo) {
+            getGeckoInfo();
+        }
+        return mGeckoHeight;
+    }
+
+    public int getGeckoWidth() {
+        if (!mGeckoInfo) {
+            getGeckoInfo();
+        }
+        return mGeckoWidth;
+    }
+
+    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");
+    }
+
+    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;
+
+        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();
+        }
+
+        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;
+
+        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;
+    }
+
+    private GLSurfaceView getSurfaceView() {
+        for (View v : mSolo.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;
+        }
+
+        // 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 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");
+                }
+
+            } 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;
+
+            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();    
+    }
+
+    /**
+     *  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());
+        }
+        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();
+        }
+    }
+
+    public static void setLogLevel(LogLevel level) {
+        mLogLevel = level;
+    }
+
+    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);
+        }
     }
-  }
 
 }
diff --git a/build/mobile/robocop/FennecNativeElement.java.in b/build/mobile/robocop/FennecNativeElement.java.in
index bbbe04304044..0785a0de2bd9 100644
--- a/build/mobile/robocop/FennecNativeElement.java.in
+++ b/build/mobile/robocop/FennecNativeElement.java.in
@@ -42,7 +42,6 @@ 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;
@@ -55,110 +54,110 @@ 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 Integer getId() {
-    return id;
-  }
-
-  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 FennecNativeElement(Integer id, Activity activity, Solo solo) {
+        mId = id;
+        mActivity = activity;
+        mSolo = solo;
     }
-  }
 
-  private Object text;
+    public Integer getId() {
+        return mId;
+    }
 
-  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 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());
                 }
-              } //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();
+            });
+        try {
+            syncQueue.take();
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        }
     }
-    if(text == null) {
-      throw new RoboCopException("getText: Text is null for view "+id);
-    }
-    return text.toString();
-  }
 
-  private boolean displayed;
+    private Object mText;
 
-  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();
+    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());
+                }
+            });
+        try {
+            syncQueue.take();
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        }
+        return mDisplayed;
     }
-    return displayed;
-  }
 
 }
diff --git a/build/mobile/robocop/FennecTalosAssert.java.in b/build/mobile/robocop/FennecTalosAssert.java.in
index 5fa85a4405f8..9a9d75511d44 100644
--- a/build/mobile/robocop/FennecTalosAssert.java.in
+++ b/build/mobile/robocop/FennecTalosAssert.java.in
@@ -39,52 +39,40 @@ package @ANDROID_PACKAGE_NAME@;
 
 
 public class FennecTalosAssert implements Assert {
-  
-  public FennecTalosAssert() {
-  }
+    
+    public FennecTalosAssert() { }
 
-  // Write information to a logfile and logcat
-  public void dumpLog(String message)
-  {
-    FennecNativeDriver.log(FennecNativeDriver.LogLevel.LOG_LEVEL_INFO, message);
-  }
+    /**
+     *  Write information to a logfile and logcat
+     */
+    public void dumpLog(String message) {
+        FennecNativeDriver.log(FennecNativeDriver.LogLevel.LOG_LEVEL_INFO, message);
+    }
 
-  // Set the filename used for dumpLog.
-  public void setLogFile(String filename)
-  {
-    FennecNativeDriver.setLogFile(filename);
-  }
+    /**
+     *  Set the filename used for dumpLog.
+     */
+    public void setLogFile(String filename) {
+        FennecNativeDriver.setLogFile(filename);
+    }
 
-  public void setTestName(String testName)
-  {
-  }
+    public void setTestName(String testName) { }
 
+    public void finalize() { }
 
-  public void finalize()
-  {
-  }
+    public void ok(boolean condition, String name, String diag) { }
 
-  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 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 ispixel(int actual, int r, int g, int b, String name) {
-  }
+    public void todo(boolean condition, String name, String diag) { }
 
-  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) { }
 }
diff --git a/build/mobile/robocop/RoboCopException.java.in b/build/mobile/robocop/RoboCopException.java.in
index de908cc32137..adc5b6af5195 100644
--- a/build/mobile/robocop/RoboCopException.java.in
+++ b/build/mobile/robocop/RoboCopException.java.in
@@ -40,20 +40,20 @@
 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);
+    }
 }
diff --git a/config/autoconf.mk.in b/config/autoconf.mk.in
index 57cca0db44bf..b8d954ccc17e 100644
--- a/config/autoconf.mk.in
+++ b/config/autoconf.mk.in
@@ -293,6 +293,7 @@ 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@
 
diff --git a/configure.in b/configure.in
index d983a02ff42c..4d9a187c4614 100644
--- a/configure.in
+++ b/configure.in
@@ -225,7 +225,14 @@ 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
@@ -4920,6 +4927,7 @@ cairo-gonk)
     MOZ_PDF_PRINTING=1
     MOZ_B2G_RIL=1
     MOZ_TOUCH=1
+    MOZ_B2G_BT=1
     ;;
 
 esac
@@ -7590,6 +7598,18 @@ if test -n "$MOZ_B2G_RIL"; then
 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 ========================================================
diff --git a/content/base/public/nsIMessageWakeupService.idl b/content/base/public/nsIMessageWakeupService.idl
deleted file mode 100644
index bd16ee8eb329..000000000000
--- 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);
-};
-
diff --git a/content/base/src/messageWakeupService.js b/content/base/src/messageWakeupService.js
index bdf011cde031..ffc6cfc8e940 100644
--- a/content/base/src/messageWakeupService.js
+++ b/content/base/src/messageWakeupService.js
@@ -46,7 +46,7 @@ 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: [],
 
diff --git a/content/base/src/nsAttrAndChildArray.h b/content/base/src/nsAttrAndChildArray.h
index a3e66c1e4012..2e56ac5ddfee 100644
--- a/content/base/src/nsAttrAndChildArray.h
+++ b/content/base/src/nsAttrAndChildArray.h
@@ -136,6 +136,10 @@ public:
   }
 
   PRInt64 SizeOf() const;
+  bool HasMappedAttrs() const
+  {
+    return MappedAttrCount();
+  }
 
 private:
   nsAttrAndChildArray(const nsAttrAndChildArray& aOther) MOZ_DELETE;
diff --git a/content/base/src/nsAttrValue.cpp b/content/base/src/nsAttrValue.cpp
index f0791f2b99ae..dd9b995358f0 100644
--- a/content/base/src/nsAttrValue.cpp
+++ b/content/base/src/nsAttrValue.cpp
@@ -54,6 +54,8 @@
 
 namespace css = mozilla::css;
 
+using mozilla::SVGAttrValueWrapper;
+
 #define MISC_STR_PTR(_cont) \
   reinterpret_cast<void*>((_cont)->mStringBits & NS_ATTRVALUE_POINTERVALUE_MASK)
 
@@ -76,6 +78,12 @@ nsAttrValue::nsAttrValue(const nsAString& aValue)
   SetTo(aValue);
 }
 
+nsAttrValue::nsAttrValue(nsIAtom* aValue)
+    : mBits(0)
+{
+  SetTo(aValue);
+}
+
 nsAttrValue::nsAttrValue(css::StyleRule* aValue, const nsAString* aSerialized)
     : mBits(0)
 {
@@ -260,7 +268,13 @@ nsAttrValue::SetTo(const nsAttrValue& aOther)
     }
     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;
     }
   }
@@ -290,6 +304,16 @@ nsAttrValue::SetTo(const nsAString& aValue)
   }
 }
 
+void
+nsAttrValue::SetTo(nsIAtom* aValue)
+{
+  ResetIfSet();
+  if (aValue) {
+    NS_ADDREF(aValue);
+    SetPtrValueAndType(aValue, eAtomBase);
+  }
+}
+
 void
 nsAttrValue::SetTo(PRInt16 aInt)
 {
@@ -297,6 +321,24 @@ nsAttrValue::SetTo(PRInt16 aInt)
   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)
 {
@@ -331,6 +373,115 @@ nsAttrValue::SetToSerialized(const nsAttrValue& 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)
 {
@@ -428,6 +579,73 @@ nsAttrValue::ToString(nsAString& aResult) const
       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();
@@ -614,6 +832,10 @@ nsAttrValue::HashValue() const
     }
     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;
     }
@@ -706,6 +928,16 @@ nsAttrValue::Equals(const nsAttrValue& aOther) const
     }
     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;
     }
@@ -1351,6 +1583,22 @@ nsAttrValue::ResetMiscAtomOrString()
   }
 }
 
+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()
 {
diff --git a/content/base/src/nsAttrValue.h b/content/base/src/nsAttrValue.h
index 8b797248f11e..7e90f8807506 100644
--- a/content/base/src/nsAttrValue.h
+++ b/content/base/src/nsAttrValue.h
@@ -51,6 +51,7 @@
 #include "nsCaseTreatment.h"
 #include "nsMargin.h"
 #include "nsCOMPtr.h"
+#include "SVGAttrValueWrapper.h"
 
 typedef PRUptrdiff PtrBits;
 class nsAString;
@@ -102,6 +103,7 @@ public:
   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();
@@ -123,9 +125,23 @@ public:
     // 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;
@@ -134,9 +150,29 @@ public:
 
   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.
@@ -368,10 +404,23 @@ private:
       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.
@@ -388,6 +437,8 @@ private:
   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;
@@ -502,6 +553,12 @@ 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)
 {
diff --git a/content/base/src/nsEventSource.cpp b/content/base/src/nsEventSource.cpp
index cb91fea9bdbf..b0860e281512 100644
--- a/content/base/src/nsEventSource.cpp
+++ b/content/base/src/nsEventSource.cpp
@@ -89,6 +89,7 @@ nsEventSource::nsEventSource() :
   mErrorLoadOnRedirect(false),
   mGoingToDispatchAllMessages(false),
   mWithCredentials(false),
+  mWaitingForOnStopRequest(false),
   mLastConvertionResult(NS_OK),
   mReadyState(nsIEventSource::CONNECTING),
   mScriptLine(0),
@@ -108,13 +109,19 @@ nsEventSource::~nsEventSource()
 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
@@ -599,6 +606,8 @@ nsEventSource::OnStopRequest(nsIRequest *aRequest,
                              nsISupports *aContext,
                              nsresult aStatusCode)
 {
+  mWaitingForOnStopRequest = false;
+
   if (mReadyState == nsIEventSource::CLOSED) {
     return NS_ERROR_ABORT;
   }
@@ -949,7 +958,11 @@ nsEventSource::InitChannelAndRequestEventSource()
   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
diff --git a/content/base/src/nsEventSource.h b/content/base/src/nsEventSource.h
index 4ee1c32b0045..0b04bce34e56 100644
--- a/content/base/src/nsEventSource.h
+++ b/content/base/src/nsEventSource.h
@@ -216,6 +216,7 @@ protected:
   bool mErrorLoadOnRedirect;
   bool mGoingToDispatchAllMessages;
   bool mWithCredentials;
+  bool mWaitingForOnStopRequest;
 
   // used while reading the input streams
   nsCOMPtr<nsIUnicodeDecoder> mUnicodeDecoder;
diff --git a/content/base/src/nsWebSocket.cpp b/content/base/src/nsWebSocket.cpp
index 1a6f14571203..b5b53594db3e 100644
--- a/content/base/src/nsWebSocket.cpp
+++ b/content/base/src/nsWebSocket.cpp
@@ -439,7 +439,8 @@ nsWebSocket::~nsWebSocket()
 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)
@@ -447,6 +448,11 @@ NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsWebSocket)
       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
diff --git a/content/base/src/nsXMLHttpRequest.cpp b/content/base/src/nsXMLHttpRequest.cpp
index 170b68fa4598..bb86e90fecea 100644
--- a/content/base/src/nsXMLHttpRequest.cpp
+++ b/content/base/src/nsXMLHttpRequest.cpp
@@ -445,8 +445,8 @@ nsXMLHttpRequest::nsXMLHttpRequest()
     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),
@@ -602,7 +602,8 @@ nsXMLHttpRequest::SetRequestObserver(nsIRequestObserver* 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)
@@ -614,6 +615,11 @@ NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsXMLHttpRequest)
       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
@@ -2140,6 +2146,8 @@ nsXMLHttpRequest::OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult
     return NS_OK;
   }
 
+  mWaitingForOnStopRequest = false;
+
   nsresult rv = NS_OK;
 
   // If we're loading a multipart stream of XML documents, we'll get
@@ -2774,6 +2782,9 @@ nsXMLHttpRequest::Send(nsIVariant *aBody)
     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;
diff --git a/content/base/src/nsXMLHttpRequest.h b/content/base/src/nsXMLHttpRequest.h
index 3d5a1ab84249..f25c953d6a6b 100644
--- a/content/base/src/nsXMLHttpRequest.h
+++ b/content/base/src/nsXMLHttpRequest.h
@@ -365,7 +365,7 @@ protected:
   void HandleTimeoutCallback();
 
   bool mErrorLoad;
-
+  bool mWaitingForOnStopRequest;
   bool mProgressTimerIsActive;
   bool mProgressEventWasDelayed;
   bool mIsHtml;
diff --git a/content/base/test/test_bug338679.html b/content/base/test/test_bug338679.html
index cea297736a2f..ac34bfc0156e 100644
--- a/content/base/test/test_bug338679.html
+++ b/content/base/test/test_bug338679.html
@@ -49,7 +49,7 @@ function attr_modified(ev) {
   e_prev = e_new;
   if (!recursive) {
     recursive = true;
-    e_new = "width: 0pt;";
+    e_new = "width: 0px;";
     testDiv.style.width = "0";
   } else {
     recursive = false;
diff --git a/content/canvas/src/WebGLContext.h b/content/canvas/src/WebGLContext.h
index 5f1d14181b15..5a709d0c1ea3 100644
--- a/content/canvas/src/WebGLContext.h
+++ b/content/canvas/src/WebGLContext.h
@@ -2388,6 +2388,7 @@ public:
 
     NS_DECL_ISUPPORTS
     NS_DECL_NSIWEBGLEXTENSION
+    virtual ~WebGLExtension() {}
 };
 
 inline const WebGLRectangleObject *WebGLContext::FramebufferRectangleObject() const {
diff --git a/content/html/content/src/nsDOMValidityState.h b/content/html/content/src/nsDOMValidityState.h
index 8370b45b9a26..5980757f2346 100644
--- a/content/html/content/src/nsDOMValidityState.h
+++ b/content/html/content/src/nsDOMValidityState.h
@@ -42,7 +42,7 @@
 #include "nsIConstraintValidation.h"
 
 
-class nsDOMValidityState : public nsIDOMValidityState
+class nsDOMValidityState MOZ_FINAL : public nsIDOMValidityState
 {
 public:
   NS_DECL_ISUPPORTS
diff --git a/content/html/content/src/nsHTMLInputElement.cpp b/content/html/content/src/nsHTMLInputElement.cpp
index a17eb86e732c..0c862a57b98c 100644
--- a/content/html/content/src/nsHTMLInputElement.cpp
+++ b/content/html/content/src/nsHTMLInputElement.cpp
@@ -185,7 +185,7 @@ static const nsAttrValue::EnumTable* kInputDefaultAutocomplete = &kInputAutocomp
   {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)
diff --git a/content/html/content/src/nsHTMLInputElement.h b/content/html/content/src/nsHTMLInputElement.h
index c462c85b6ea9..453b8f7a06f1 100644
--- a/content/html/content/src/nsHTMLInputElement.h
+++ b/content/html/content/src/nsHTMLInputElement.h
@@ -57,7 +57,7 @@ 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
diff --git a/content/html/content/src/nsHTMLMediaElement.cpp b/content/html/content/src/nsHTMLMediaElement.cpp
index c4cc3d966c29..fcb41e0946ca 100644
--- a/content/html/content/src/nsHTMLMediaElement.cpp
+++ b/content/html/content/src/nsHTMLMediaElement.cpp
@@ -243,10 +243,10 @@ public:
  * 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
diff --git a/content/html/content/src/nsMediaError.h b/content/html/content/src/nsMediaError.h
index 33d657f650c8..c540afd92335 100644
--- a/content/html/content/src/nsMediaError.h
+++ b/content/html/content/src/nsMediaError.h
@@ -37,8 +37,9 @@
  * ***** 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);
diff --git a/content/html/content/src/nsTimeRanges.h b/content/html/content/src/nsTimeRanges.h
index 172cadb5b85f..8359b2378deb 100644
--- a/content/html/content/src/nsTimeRanges.h
+++ b/content/html/content/src/nsTimeRanges.h
@@ -45,7 +45,7 @@
 
 // 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
diff --git a/content/media/nsBuiltinDecoder.cpp b/content/media/nsBuiltinDecoder.cpp
index c4b0c294b2ed..a0f40e5802f3 100644
--- a/content/media/nsBuiltinDecoder.cpp
+++ b/content/media/nsBuiltinDecoder.cpp
@@ -216,6 +216,7 @@ nsresult nsBuiltinDecoder::Load(MediaResource* aResource,
     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     mDecoderStateMachine->SetSeekable(mSeekable);
     mDecoderStateMachine->SetDuration(mDuration);
+    mDecoderStateMachine->SetVolume(mInitialVolume);
     
     if (mFrameBufferLength > 0) {
       // The valid mFrameBufferLength value was specified earlier
diff --git a/content/svg/content/src/DOMSVGLength.cpp b/content/svg/content/src/DOMSVGLength.cpp
index 8e0de116e52d..ada2143a2042 100644
--- a/content/svg/content/src/DOMSVGLength.cpp
+++ b/content/svg/content/src/DOMSVGLength.cpp
@@ -156,8 +156,14 @@ DOMSVGLength::SetValue(float aUserUnitValue)
   // 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();
       }
@@ -195,8 +201,12 @@ DOMSVGLength::SetValueInSpecifiedUnits(float aValue)
   }
 
   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();
     }
@@ -218,8 +228,12 @@ DOMSVGLength::SetValueAsString(const nsAString& 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();
     }
@@ -259,8 +273,13 @@ DOMSVGLength::NewValueSpecifiedUnits(PRUint16 aUnit, float aValue)
     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();
     }
@@ -282,7 +301,12 @@ DOMSVGLength::ConvertToSpecifiedUnits(PRUint16 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 {
diff --git a/content/svg/content/src/DOMSVGLengthList.cpp b/content/svg/content/src/DOMSVGLengthList.cpp
index b59930d9bdd6..f5b46c46e0a6 100644
--- a/content/svg/content/src/DOMSVGLengthList.cpp
+++ b/content/svg/content/src/DOMSVGLengthList.cpp
@@ -171,6 +171,7 @@ DOMSVGLengthList::Clear()
   }
 
   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:
@@ -178,7 +179,7 @@ DOMSVGLengthList::Clear()
 
     mItems.Clear();
     InternalList().Clear();
-    Element()->DidChangeLengthList(AttrEnum(), true);
+    Element()->DidChangeLengthList(AttrEnum(), emptyOrOldValue);
     if (mAList->IsAnimating()) {
       Element()->AnimationNeedsResample();
     }
@@ -256,6 +257,7 @@ DOMSVGLengthList::InsertItemBefore(nsIDOMSVGLength *newItem,
     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);
 
@@ -269,7 +271,7 @@ DOMSVGLengthList::InsertItemBefore(nsIDOMSVGLength *newItem,
 
   UpdateListIndicesFromIndex(mItems, index + 1);
 
-  Element()->DidChangeLengthList(AttrEnum(), true);
+  Element()->DidChangeLengthList(AttrEnum(), emptyOrOldValue);
   if (mAList->IsAnimating()) {
     Element()->AnimationNeedsResample();
   }
@@ -298,6 +300,7 @@ DOMSVGLengthList::ReplaceItem(nsIDOMSVGLength *newItem,
     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:
@@ -311,7 +314,7 @@ DOMSVGLengthList::ReplaceItem(nsIDOMSVGLength *newItem,
   // 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();
   }
@@ -332,6 +335,7 @@ DOMSVGLengthList::RemoveItem(PRUint32 index,
     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.
@@ -350,7 +354,7 @@ DOMSVGLengthList::RemoveItem(PRUint32 index,
 
   UpdateListIndicesFromIndex(mItems, index);
 
-  Element()->DidChangeLengthList(AttrEnum(), true);
+  Element()->DidChangeLengthList(AttrEnum(), emptyOrOldValue);
   if (mAList->IsAnimating()) {
     Element()->AnimationNeedsResample();
   }
diff --git a/content/svg/content/src/DOMSVGNumber.cpp b/content/svg/content/src/DOMSVGNumber.cpp
index 2648f32b1d4b..a9f815964a2f 100644
--- a/content/svg/content/src/DOMSVGNumber.cpp
+++ b/content/svg/content/src/DOMSVGNumber.cpp
@@ -123,8 +123,12 @@ DOMSVGNumber::SetValue(float aValue)
   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();
     }
diff --git a/content/svg/content/src/DOMSVGNumberList.cpp b/content/svg/content/src/DOMSVGNumberList.cpp
index 6e9d8b804584..1f458180f52f 100644
--- a/content/svg/content/src/DOMSVGNumberList.cpp
+++ b/content/svg/content/src/DOMSVGNumberList.cpp
@@ -171,6 +171,7 @@ DOMSVGNumberList::Clear()
   }
 
   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:
@@ -178,7 +179,7 @@ DOMSVGNumberList::Clear()
 
     mItems.Clear();
     InternalList().Clear();
-    Element()->DidChangeNumberList(AttrEnum(), true);
+    Element()->DidChangeNumberList(AttrEnum(), emptyOrOldValue);
     if (mAList->IsAnimating()) {
       Element()->AnimationNeedsResample();
     }
@@ -256,6 +257,7 @@ DOMSVGNumberList::InsertItemBefore(nsIDOMSVGNumber *newItem,
     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);
 
@@ -269,7 +271,7 @@ DOMSVGNumberList::InsertItemBefore(nsIDOMSVGNumber *newItem,
 
   UpdateListIndicesFromIndex(mItems, index + 1);
 
-  Element()->DidChangeNumberList(AttrEnum(), true);
+  Element()->DidChangeNumberList(AttrEnum(), emptyOrOldValue);
   if (mAList->IsAnimating()) {
     Element()->AnimationNeedsResample();
   }
@@ -298,6 +300,7 @@ DOMSVGNumberList::ReplaceItem(nsIDOMSVGNumber *newItem,
     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:
@@ -311,7 +314,7 @@ DOMSVGNumberList::ReplaceItem(nsIDOMSVGNumber *newItem,
   // 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();
   }
@@ -340,6 +343,7 @@ DOMSVGNumberList::RemoveItem(PRUint32 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();
@@ -350,7 +354,7 @@ DOMSVGNumberList::RemoveItem(PRUint32 index,
 
   UpdateListIndicesFromIndex(mItems, index);
 
-  Element()->DidChangeNumberList(AttrEnum(), true);
+  Element()->DidChangeNumberList(AttrEnum(), emptyOrOldValue);
   if (mAList->IsAnimating()) {
     Element()->AnimationNeedsResample();
   }
diff --git a/content/svg/content/src/DOMSVGPathSeg.cpp b/content/svg/content/src/DOMSVGPathSeg.cpp
index 53c5680923ff..36c90033217e 100644
--- a/content/svg/content/src/DOMSVGPathSeg.cpp
+++ b/content/svg/content/src/DOMSVGPathSeg.cpp
@@ -245,9 +245,13 @@ DOMSVGPathSeg::IndexIsValid()
     }                                                                         \
     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();                                  \
       }                                                                       \
diff --git a/content/svg/content/src/DOMSVGPathSegList.cpp b/content/svg/content/src/DOMSVGPathSegList.cpp
index ff0c9ad645dc..b2ae7ab3ecb7 100644
--- a/content/svg/content/src/DOMSVGPathSegList.cpp
+++ b/content/svg/content/src/DOMSVGPathSegList.cpp
@@ -269,6 +269,7 @@ DOMSVGPathSegList::Clear()
   }
 
   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!
@@ -285,7 +286,7 @@ DOMSVGPathSegList::Clear()
     }
 
     InternalList().Clear();
-    Element()->DidChangePathSegList(true);
+    Element()->DidChangePathSegList(emptyOrOldValue);
     if (AttrIsAnimating()) {
       Element()->AnimationNeedsResample();
     }
@@ -371,6 +372,7 @@ DOMSVGPathSegList::InsertItemBefore(nsIDOMSVGPathSeg *aNewItem,
     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);
 
@@ -387,7 +389,7 @@ DOMSVGPathSegList::InsertItemBefore(nsIDOMSVGPathSeg *aNewItem,
 
   UpdateListIndicesFromIndex(aIndex + 1, argCount + 1);
 
-  Element()->DidChangePathSegList(true);
+  Element()->DidChangePathSegList(emptyOrOldValue);
   if (AttrIsAnimating()) {
     Element()->AnimationNeedsResample();
   }
@@ -416,6 +418,7 @@ DOMSVGPathSegList::ReplaceItem(nsIDOMSVGPathSeg *aNewItem,
     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:
@@ -451,7 +454,7 @@ DOMSVGPathSegList::ReplaceItem(nsIDOMSVGPathSeg *aNewItem,
     }
   }
 
-  Element()->DidChangePathSegList(true);
+  Element()->DidChangePathSegList(emptyOrOldValue);
   if (AttrIsAnimating()) {
     Element()->AnimationNeedsResample();
   }
@@ -474,6 +477,7 @@ DOMSVGPathSegList::RemoveItem(PRUint32 aIndex,
   // 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();
@@ -493,7 +497,7 @@ DOMSVGPathSegList::RemoveItem(PRUint32 aIndex,
 
   UpdateListIndicesFromIndex(aIndex, -(argCount + 1));
 
-  Element()->DidChangePathSegList(true);
+  Element()->DidChangePathSegList(emptyOrOldValue);
   if (AttrIsAnimating()) {
     Element()->AnimationNeedsResample();
   }
diff --git a/content/svg/content/src/DOMSVGPoint.cpp b/content/svg/content/src/DOMSVGPoint.cpp
index ba3d6ef946b9..dbe96022c94b 100644
--- a/content/svg/content/src/DOMSVGPoint.cpp
+++ b/content/svg/content/src/DOMSVGPoint.cpp
@@ -98,8 +98,12 @@ DOMSVGPoint::SetX(float aX)
   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();
     }
@@ -129,8 +133,12 @@ DOMSVGPoint::SetY(float aY)
   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();
     }
diff --git a/content/svg/content/src/DOMSVGPointList.cpp b/content/svg/content/src/DOMSVGPointList.cpp
index 18baadb595c3..aa660fe41876 100644
--- a/content/svg/content/src/DOMSVGPointList.cpp
+++ b/content/svg/content/src/DOMSVGPointList.cpp
@@ -213,6 +213,7 @@ DOMSVGPointList::Clear()
   }
 
   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!
@@ -229,7 +230,7 @@ DOMSVGPointList::Clear()
     }
 
     InternalList().Clear();
-    Element()->DidChangePointList(true);
+    Element()->DidChangePointList(emptyOrOldValue);
     if (AttrIsAnimating()) {
       Element()->AnimationNeedsResample();
     }
@@ -307,6 +308,7 @@ DOMSVGPointList::InsertItemBefore(nsIDOMSVGPoint *aNewItem,
     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);
 
@@ -320,7 +322,7 @@ DOMSVGPointList::InsertItemBefore(nsIDOMSVGPoint *aNewItem,
 
   UpdateListIndicesFromIndex(mItems, aIndex + 1);
 
-  Element()->DidChangePointList(true);
+  Element()->DidChangePointList(emptyOrOldValue);
   if (AttrIsAnimating()) {
     Element()->AnimationNeedsResample();
   }
@@ -349,6 +351,7 @@ DOMSVGPointList::ReplaceItem(nsIDOMSVGPoint *aNewItem,
     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:
@@ -362,7 +365,7 @@ DOMSVGPointList::ReplaceItem(nsIDOMSVGPoint *aNewItem,
   // would end up reading bad data from InternalList()!
   domItem->InsertingIntoList(this, aIndex, IsAnimValList());
 
-  Element()->DidChangePointList(true);
+  Element()->DidChangePointList(emptyOrOldValue);
   if (AttrIsAnimating()) {
     Element()->AnimationNeedsResample();
   }
@@ -383,6 +386,7 @@ DOMSVGPointList::RemoveItem(PRUint32 aIndex,
     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.
@@ -401,7 +405,7 @@ DOMSVGPointList::RemoveItem(PRUint32 aIndex,
 
   UpdateListIndicesFromIndex(mItems, aIndex);
 
-  Element()->DidChangePointList(true);
+  Element()->DidChangePointList(emptyOrOldValue);
   if (AttrIsAnimating()) {
     Element()->AnimationNeedsResample();
   }
diff --git a/content/svg/content/src/DOMSVGStringList.cpp b/content/svg/content/src/DOMSVGStringList.cpp
index 8fd2a53d605c..7f0966b1b55a 100644
--- a/content/svg/content/src/DOMSVGStringList.cpp
+++ b/content/svg/content/src/DOMSVGStringList.cpp
@@ -109,9 +109,12 @@ NS_IMETHODIMP
 DOMSVGStringList::Clear()
 {
   if (InternalList().IsExplicitlySet()) {
+    nsAttrValue emptyOrOldValue =
+      mElement->WillChangeStringList(mIsConditionalProcessingAttribute,
+                                     mAttrEnum);
     InternalList().Clear();
     mElement->DidChangeStringList(mIsConditionalProcessingAttribute,
-                                  mAttrEnum);
+                                  mAttrEnum, emptyOrOldValue);
   }
   return NS_OK;
 }
@@ -151,9 +154,13 @@ DOMSVGStringList::InsertItemBefore(const nsAString & newItem,
     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;
 }
@@ -171,9 +178,13 @@ DOMSVGStringList::ReplaceItem(const nsAString & newItem,
   }
 
   _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;
 }
 
@@ -185,9 +196,13 @@ DOMSVGStringList::RemoveItem(PRUint32 index,
     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;
 }
 
diff --git a/content/svg/content/src/DOMSVGStringList.h b/content/svg/content/src/DOMSVGStringList.h
index 6bb74f3c5625..1437687db758 100644
--- a/content/svg/content/src/DOMSVGStringList.h
+++ b/content/svg/content/src/DOMSVGStringList.h
@@ -111,8 +111,6 @@ private:
 
   ~DOMSVGStringList();
 
-  void DidChangeStringList(PRUint8 aAttrEnum, bool aDoSetAttr);
-
   SVGStringList &InternalList();
 
   // Strong ref to our element to keep it alive.
diff --git a/content/svg/content/src/DOMSVGTests.cpp b/content/svg/content/src/DOMSVGTests.cpp
index cdfa3585214b..77e4bdcddafa 100644
--- a/content/svg/content/src/DOMSVGTests.cpp
+++ b/content/svg/content/src/DOMSVGTests.cpp
@@ -48,13 +48,18 @@ 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)
@@ -96,8 +101,8 @@ DOMSVGTests::HasExtension(const nsAString & extension, bool *_retval)
 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;
     }
   }
@@ -222,10 +227,9 @@ 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();
       }
@@ -236,20 +240,11 @@ DOMSVGTests::ParseConditionalProcessingAttribute(nsIAtom* aAttribute,
   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;
@@ -257,22 +252,18 @@ DOMSVGTests::UnsetAttr(const nsIAtom* aAttribute)
   }
 }
 
-void
-DOMSVGTests::DidChangeStringList(PRUint8 aAttrEnum)
+nsIAtom*
+DOMSVGTests::GetAttrName(PRUint8 aAttrEnum) const
 {
-  NS_ASSERTION(aAttrEnum < ArrayLength(sStringListInfo), "aAttrEnum out of range");
+  return *sStringListNames[aAttrEnum];
+}
 
-  nsCOMPtr<nsSVGElement> element = do_QueryInterface(this);
-
-  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
@@ -281,7 +272,7 @@ 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();
diff --git a/content/svg/content/src/DOMSVGTests.h b/content/svg/content/src/DOMSVGTests.h
index f1332a8ac226..7fc3cb232c32 100644
--- a/content/svg/content/src/DOMSVGTests.h
+++ b/content/svg/content/src/DOMSVGTests.h
@@ -59,6 +59,8 @@ public:
   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
@@ -103,30 +105,20 @@ public:
          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__
diff --git a/content/svg/content/src/DOMSVGTransform.cpp b/content/svg/content/src/DOMSVGTransform.cpp
index 94d36007c250..8f8956464c36 100644
--- a/content/svg/content/src/DOMSVGTransform.cpp
+++ b/content/svg/content/src/DOMSVGTransform.cpp
@@ -173,9 +173,7 @@ DOMSVGTransform::SetMatrix(nsIDOMSVGMatrix *matrix)
   if (!domMatrix)
     return NS_ERROR_DOM_SVG_WRONG_TYPE_ERR;
 
-  Transform().SetMatrix(domMatrix->Matrix());
-  NotifyElementOfChange();
-
+  SetMatrix(domMatrix->Matrix());
   return NS_OK;
 }
 
@@ -188,8 +186,14 @@ DOMSVGTransform::SetTranslate(float tx, float ty)
   }
   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;
 }
@@ -203,8 +207,14 @@ DOMSVGTransform::SetScale(float sx, float sy)
   }
   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;
 }
@@ -218,8 +228,17 @@ DOMSVGTransform::SetRotate(float angle, float cx, float cy)
   }
   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;
 }
@@ -233,10 +252,16 @@ DOMSVGTransform::SetSkewX(float angle)
   }
   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;
 }
@@ -250,10 +275,16 @@ DOMSVGTransform::SetSkewY(float angle)
   }
   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;
 }
@@ -324,8 +355,15 @@ 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
@@ -341,10 +379,10 @@ DOMSVGTransform::ClearMatrixTearoff(DOMSVGMatrix* aMatrix)
 // Implementation helpers
 
 void
-DOMSVGTransform::NotifyElementOfChange()
+DOMSVGTransform::NotifyElementDidChange(const nsAttrValue& aEmptyOrOldValue)
 {
   if (HasOwner()) {
-    Element()->DidChangeTransformList(true);
+    Element()->DidChangeTransformList(aEmptyOrOldValue);
     if (mList->mAList->IsAnimating()) {
       Element()->AnimationNeedsResample();
     }
diff --git a/content/svg/content/src/DOMSVGTransform.h b/content/svg/content/src/DOMSVGTransform.h
index 58654e0b213b..7f1f6ec6fa82 100644
--- a/content/svg/content/src/DOMSVGTransform.h
+++ b/content/svg/content/src/DOMSVGTransform.h
@@ -206,7 +206,8 @@ private:
   SVGTransform& Transform() {
     return HasOwner() ? InternalItem() : *mTransform;
   }
-  void NotifyElementOfChange();
+  inline nsAttrValue NotifyElementWillChange();
+  void NotifyElementDidChange(const nsAttrValue& aEmptyOrOldValue);
 
   nsRefPtr<DOMSVGTransformList> mList;
 
@@ -235,6 +236,16 @@ private:
 
 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
diff --git a/content/svg/content/src/DOMSVGTransformList.cpp b/content/svg/content/src/DOMSVGTransformList.cpp
index 6484e15b5c5f..c8c89f18c020 100644
--- a/content/svg/content/src/DOMSVGTransformList.cpp
+++ b/content/svg/content/src/DOMSVGTransformList.cpp
@@ -184,6 +184,7 @@ DOMSVGTransformList::Clear()
   }
 
   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:
@@ -191,7 +192,7 @@ DOMSVGTransformList::Clear()
 
     mItems.Clear();
     InternalList().Clear();
-    Element()->DidChangeTransformList(true);
+    Element()->DidChangeTransformList(emptyOrOldValue);
     if (mAList->IsAnimating()) {
       Element()->AnimationNeedsResample();
     }
@@ -272,6 +273,7 @@ DOMSVGTransformList::InsertItemBefore(nsIDOMSVGTransform *newItem,
     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);
 
@@ -285,7 +287,7 @@ DOMSVGTransformList::InsertItemBefore(nsIDOMSVGTransform *newItem,
 
   UpdateListIndicesFromIndex(mItems, index + 1);
 
-  Element()->DidChangeTransformList(true);
+  Element()->DidChangeTransformList(emptyOrOldValue);
   if (mAList->IsAnimating()) {
     Element()->AnimationNeedsResample();
   }
@@ -316,6 +318,7 @@ DOMSVGTransformList::ReplaceItem(nsIDOMSVGTransform *newItem,
     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:
@@ -329,7 +332,7 @@ DOMSVGTransformList::ReplaceItem(nsIDOMSVGTransform *newItem,
   // would end up reading bad data from InternalList()!
   domItem->InsertingIntoList(this, index, IsAnimValList());
 
-  Element()->DidChangeTransformList(true);
+  Element()->DidChangeTransformList(emptyOrOldValue);
   if (mAList->IsAnimating()) {
     Element()->AnimationNeedsResample();
   }
@@ -350,6 +353,7 @@ DOMSVGTransformList::RemoveItem(PRUint32 index, nsIDOMSVGTransform **_retval)
     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.
@@ -368,7 +372,7 @@ DOMSVGTransformList::RemoveItem(PRUint32 index, nsIDOMSVGTransform **_retval)
 
   UpdateListIndicesFromIndex(mItems, index);
 
-  Element()->DidChangeTransformList(true);
+  Element()->DidChangeTransformList(emptyOrOldValue);
   if (mAList->IsAnimating()) {
     Element()->AnimationNeedsResample();
   }
diff --git a/content/svg/content/src/Makefile.in b/content/svg/content/src/Makefile.in
index f27cb4d45170..601a5e30d1c6 100644
--- a/content/svg/content/src/Makefile.in
+++ b/content/svg/content/src/Makefile.in
@@ -146,6 +146,7 @@ CPPSRCS		= \
 		nsSVGAnimationElement.cpp \
 		nsSVGMpathElement.cpp \
 		nsSVGSetElement.cpp \
+		SVGAttrValueWrapper.cpp \
 		SVGIntegerPairSMILType.cpp \
 		SVGLengthListSMILType.cpp \
 		SVGMotionSMILType.cpp \
@@ -169,6 +170,7 @@ FORCE_STATIC_LIB = 1
 EXPORTS =  			\
 	nsSVGFeatures.h            \
 	nsSVGRect.h                \
+	SVGAttrValueWrapper.h      \
 	$(NULL)
 
 include $(topsrcdir)/config/rules.mk
diff --git a/content/svg/content/src/SVGAnimatedPreserveAspectRatio.cpp b/content/svg/content/src/SVGAnimatedPreserveAspectRatio.cpp
index 9905e3a4831b..429de200e136 100644
--- a/content/svg/content/src/SVGAnimatedPreserveAspectRatio.cpp
+++ b/content/svg/content/src/SVGAnimatedPreserveAspectRatio.cpp
@@ -250,7 +250,8 @@ SVGAnimatedPreserveAspectRatio::SetBaseValueString(
 }
 
 void
-SVGAnimatedPreserveAspectRatio::GetBaseValueString(nsAString & aValueAsString)
+SVGAnimatedPreserveAspectRatio::GetBaseValueString(
+  nsAString& aValueAsString) const
 {
   nsAutoString tmpString;
 
@@ -276,12 +277,17 @@ 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();
   }
@@ -293,12 +299,17 @@ 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();
   }
diff --git a/content/svg/content/src/SVGAnimatedPreserveAspectRatio.h b/content/svg/content/src/SVGAnimatedPreserveAspectRatio.h
index b8c353481d0a..5b2e8396aef2 100644
--- a/content/svg/content/src/SVGAnimatedPreserveAspectRatio.h
+++ b/content/svg/content/src/SVGAnimatedPreserveAspectRatio.h
@@ -118,7 +118,7 @@ public:
 
   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);
diff --git a/content/svg/content/src/SVGAttrValueWrapper.cpp b/content/svg/content/src/SVGAttrValueWrapper.cpp
new file mode 100644
index 000000000000..af3f5455c1c6
--- /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);
+}
diff --git a/content/svg/content/src/SVGAttrValueWrapper.h b/content/svg/content/src/SVGAttrValueWrapper.h
new file mode 100644
index 000000000000..b0419d570999
--- /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__
diff --git a/content/svg/content/src/SVGStringList.cpp b/content/svg/content/src/SVGStringList.cpp
index 6cb8a69fd1b2..a8a608135da1 100644
--- a/content/svg/content/src/SVGStringList.cpp
+++ b/content/svg/content/src/SVGStringList.cpp
@@ -62,14 +62,14 @@ SVGStringList::CopyFrom(const SVGStringList& rhs)
 }
 
 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(' ');
@@ -78,11 +78,11 @@ SVGStringList::GetValue(nsAString& aValue, bool aIsCommaSeparated) const
 }
 
 nsresult
-SVGStringList::SetValue(const nsAString& aValue, bool aIsCommaSeparated)
+SVGStringList::SetValue(const nsAString& aValue)
 {
   SVGStringList temp;
 
-  if (aIsCommaSeparated) {
+  if (mIsCommaSeparated) {
     nsCharSeparatedTokenizerTemplate<IsSVGWhitespace>
       tokenizer(aValue, ',');
 
diff --git a/content/svg/content/src/SVGStringList.h b/content/svg/content/src/SVGStringList.h
index 4548564c95da..5452d944f8d3 100644
--- a/content/svg/content/src/SVGStringList.h
+++ b/content/svg/content/src/SVGStringList.h
@@ -54,10 +54,13 @@ class SVGStringList
 
 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();
@@ -65,7 +68,7 @@ public:
   }
 
   /// 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();
@@ -91,9 +94,8 @@ public:
     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; }
 
@@ -168,6 +170,7 @@ protected:
    */
   nsTArray<nsString> mStrings;
   bool mIsSet;
+  bool mIsCommaSeparated;
 };
 
 } // namespace mozilla
diff --git a/content/svg/content/src/SVGTransform.h b/content/svg/content/src/SVGTransform.h
index b4d661e9e91a..51bf7c660347 100644
--- a/content/svg/content/src/SVGTransform.h
+++ b/content/svg/content/src/SVGTransform.h
@@ -97,7 +97,6 @@ public:
   nsresult SetSkewX(float aAngle);
   nsresult SetSkewY(float aAngle);
 
-protected:
   static bool MatricesEqual(const gfxMatrix& a, const gfxMatrix& b)
   {
     return a.xx == b.xx &&
@@ -108,6 +107,7 @@ protected:
            a.y0 == b.y0;
   }
 
+protected:
   gfxMatrix mMatrix;
   float mAngle, mOriginX, mOriginY;
   PRUint16 mType;
diff --git a/content/svg/content/src/nsSVGAngle.cpp b/content/svg/content/src/nsSVGAngle.cpp
index 087d00f2ee85..689799708365 100644
--- a/content/svg/content/src/nsSVGAngle.cpp
+++ b/content/svg/content/src/nsSVGAngle.cpp
@@ -72,7 +72,7 @@ public:
   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;
     }
 
@@ -258,6 +258,11 @@ void
 nsSVGAngle::SetBaseValueInSpecifiedUnits(float aValue,
                                          nsSVGElement *aSVGElement)
 {
+  if (mBaseVal == aValue) {
+    return;
+  }
+
+  nsAttrValue emptyOrOldValue = aSVGElement->WillChangeAngle(mAttrEnum);
   mBaseVal = aValue;
   if (!mIsAnimated) {
     mAnimVal = mBaseVal;
@@ -265,7 +270,7 @@ nsSVGAngle::SetBaseValueInSpecifiedUnits(float aValue,
   else {
     aSVGElement->AnimationNeedsResample();
   }
-  aSVGElement->DidChangeAngle(mAttrEnum, true);
+  aSVGElement->DidChangeAngle(mAttrEnum, emptyOrOldValue);
 }
 
 nsresult
@@ -275,9 +280,19 @@ nsSVGAngle::ConvertToSpecifiedUnits(PRUint16 unitType,
   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;
 }
 
@@ -291,6 +306,13 @@ nsSVGAngle::NewValueSpecifiedUnits(PRUint16 unitType,
   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) {
@@ -301,7 +323,7 @@ nsSVGAngle::NewValueSpecifiedUnits(PRUint16 unitType,
     aSVGElement->AnimationNeedsResample();
   }
   if (aSVGElement) {
-    aSVGElement->DidChangeAngle(mAttrEnum, true);
+    aSVGElement->DidChangeAngle(mAttrEnum, emptyOrOldValue);
   }
   return NS_OK;
 }
@@ -342,7 +364,14 @@ nsSVGAngle::SetBaseValueString(const nsAString &aValueAsString,
   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) {
@@ -353,27 +382,36 @@ nsSVGAngle::SetBaseValueString(const nsAString &aValueAsString,
     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;
@@ -381,8 +419,8 @@ nsSVGAngle::SetBaseValue(float aValue, nsSVGElement *aSVGElement)
   else {
     aSVGElement->AnimationNeedsResample();
   }
-  if (aSVGElement) {
-    aSVGElement->DidChangeAngle(mAttrEnum, true);
+  if (aSVGElement && aDoSetAttr) {
+    aSVGElement->DidChangeAngle(mAttrEnum, emptyOrOldValue);
   }
 }
 
diff --git a/content/svg/content/src/nsSVGAngle.h b/content/svg/content/src/nsSVGAngle.h
index ff518f1834c5..10ab5d3e7b05 100644
--- a/content/svg/content/src/nsSVGAngle.h
+++ b/content/svg/content/src/nsSVGAngle.h
@@ -67,15 +67,15 @@ public:
   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; }
@@ -125,7 +125,7 @@ public:
     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; }
diff --git a/content/svg/content/src/nsSVGBoolean.cpp b/content/svg/content/src/nsSVGBoolean.cpp
index 2b7db25d2215..c31fca96e4df 100644
--- a/content/svg/content/src/nsSVGBoolean.cpp
+++ b/content/svg/content/src/nsSVGBoolean.cpp
@@ -70,13 +70,26 @@ GetValueFromString(const nsAString &aValueAsString,
   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;
   }
@@ -95,30 +108,28 @@ nsSVGBoolean::SetBaseValueString(const nsAString &aValueAsString,
   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
diff --git a/content/svg/content/src/nsSVGBoolean.h b/content/svg/content/src/nsSVGBoolean.h
index 4ca77411d0aa..f649c37ce478 100644
--- a/content/svg/content/src/nsSVGBoolean.h
+++ b/content/svg/content/src/nsSVGBoolean.h
@@ -58,9 +58,8 @@ public:
     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
diff --git a/content/svg/content/src/nsSVGElement.cpp b/content/svg/content/src/nsSVGElement.cpp
index 61c693aa4dd9..1b8d052dc594 100644
--- a/content/svg/content/src/nsSVGElement.cpp
+++ b/content/svg/content/src/nsSVGElement.cpp
@@ -89,6 +89,7 @@
 #include <stdarg.h>
 #include "nsSMILMappedAttribute.h"
 #include "SVGMotionSMILAttr.h"
+#include "nsAttrValueOrString.h"
 
 using namespace mozilla;
 
@@ -262,6 +263,15 @@ 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
@@ -290,6 +300,8 @@ nsSVGElement::ParseAttribute(PRInt32 aNamespaceID,
 {
   nsresult rv = NS_OK;
   bool foundMatch = false;
+  bool didSetResult = false;
+
   if (aNamespaceID == kNameSpaceID_None) {
 
     // Check for nsSVGLength2 attribute
@@ -301,6 +313,9 @@ nsSVGElement::ParseAttribute(PRInt32 aNamespaceID,
         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;
@@ -315,6 +330,10 @@ nsSVGElement::ParseAttribute(PRInt32 aNamespaceID,
           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;
@@ -330,6 +349,10 @@ nsSVGElement::ParseAttribute(PRInt32 aNamespaceID,
           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;
@@ -342,11 +365,12 @@ nsSVGElement::ParseAttribute(PRInt32 aNamespaceID,
       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;
         }
       }
@@ -357,11 +381,12 @@ nsSVGElement::ParseAttribute(PRInt32 aNamespaceID,
       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;
         }
       }
@@ -375,6 +400,9 @@ nsSVGElement::ParseAttribute(PRInt32 aNamespaceID,
           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;
@@ -390,6 +418,9 @@ nsSVGElement::ParseAttribute(PRInt32 aNamespaceID,
           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;
@@ -405,6 +436,9 @@ nsSVGElement::ParseAttribute(PRInt32 aNamespaceID,
           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;
@@ -417,9 +451,13 @@ nsSVGElement::ParseAttribute(PRInt32 aNamespaceID,
       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;
@@ -435,6 +473,9 @@ nsSVGElement::ParseAttribute(PRInt32 aNamespaceID,
           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;
@@ -447,9 +488,13 @@ nsSVGElement::ParseAttribute(PRInt32 aNamespaceID,
       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;
@@ -462,9 +507,13 @@ nsSVGElement::ParseAttribute(PRInt32 aNamespaceID,
       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;
@@ -486,9 +535,12 @@ nsSVGElement::ParseAttribute(PRInt32 aNamespaceID,
       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;
@@ -504,6 +556,9 @@ nsSVGElement::ParseAttribute(PRInt32 aNamespaceID,
           rv = viewBox->SetBaseValueString(aValue, this);
           if (NS_FAILED(rv)) {
             viewBox->Init();
+          } else {
+            aResult.SetTo(*viewBox, &aValue);
+            didSetResult = true;
           }
           foundMatch = true;
         }
@@ -515,6 +570,9 @@ nsSVGElement::ParseAttribute(PRInt32 aNamespaceID,
           rv = preserveAspectRatio->SetBaseValueString(aValue, this);
           if (NS_FAILED(rv)) {
             preserveAspectRatio->Init();
+          } else {
+            aResult.SetTo(*preserveAspectRatio, &aValue);
+            didSetResult = true;
           }
           foundMatch = true;
         }
@@ -525,6 +583,9 @@ nsSVGElement::ParseAttribute(PRInt32 aNamespaceID,
           rv = transformList->SetBaseValueString(aValue);
           if (NS_FAILED(rv)) {
             transformList->ClearBaseValue();
+          } else {
+            aResult.SetTo(transformList->GetBaseValue(), &aValue);
+            didSetResult = true;
           }
           foundMatch = true;
         }
@@ -550,7 +611,9 @@ nsSVGElement::ParseAttribute(PRInt32 aNamespaceID,
       ReportAttributeParseFailure(OwnerDoc(), aAttribute, aValue);
       return false;
     }
-    aResult.SetTo(aValue);
+    if (!didSetResult) {
+      aResult.SetTo(aValue);
+    }
     return true;
   }
 
@@ -584,8 +647,8 @@ nsSVGElement::UnsetAttrInternal(PRInt32 aNamespaceID, nsIAtom* aName,
 
     for (PRUint32 i = 0; i < lenInfo.mLengthCount; i++) {
       if (aName == *lenInfo.mLengthInfo[i].mName) {
+        MaybeSerializeAttrBeforeRemoval(aName, aNotify);
         lenInfo.Reset(i);
-        DidChangeLength(i, false);
         return;
       }
     }
@@ -595,8 +658,8 @@ nsSVGElement::UnsetAttrInternal(PRInt32 aNamespaceID, nsIAtom* aName,
 
     for (PRUint32 i = 0; i < lengthListInfo.mLengthListCount; i++) {
       if (aName == *lengthListInfo.mLengthListInfo[i].mName) {
+        MaybeSerializeAttrBeforeRemoval(aName, aNotify);
         lengthListInfo.Reset(i);
-        DidChangeLengthList(i, false);
         return;
       }
     }
@@ -606,8 +669,8 @@ nsSVGElement::UnsetAttrInternal(PRInt32 aNamespaceID, nsIAtom* aName,
 
     for (PRUint32 i = 0; i < numberListInfo.mNumberListCount; i++) {
       if (aName == *numberListInfo.mNumberListInfo[i].mName) {
+        MaybeSerializeAttrBeforeRemoval(aName, aNotify);
         numberListInfo.Reset(i);
-        DidChangeNumberList(i, false);
         return;
       }
     }
@@ -616,6 +679,7 @@ nsSVGElement::UnsetAttrInternal(PRInt32 aNamespaceID, nsIAtom* aName,
     if (GetPointListAttrName() == aName) {
       SVGAnimatedPointList *pointList = GetAnimatedPointList();
       if (pointList) {
+        MaybeSerializeAttrBeforeRemoval(aName, aNotify);
         pointList->ClearBaseValue();
         return;
       }
@@ -625,8 +689,8 @@ nsSVGElement::UnsetAttrInternal(PRInt32 aNamespaceID, nsIAtom* aName,
     if (GetPathDataAttrName() == aName) {
       SVGAnimatedPathSegList *segList = GetAnimPathSegList();
       if (segList) {
+        MaybeSerializeAttrBeforeRemoval(aName, aNotify);
         segList->ClearBaseValue();
-        DidChangePathSegList(false);
         return;
       }
     }
@@ -637,7 +701,6 @@ nsSVGElement::UnsetAttrInternal(PRInt32 aNamespaceID, nsIAtom* aName,
     for (PRUint32 i = 0; i < numInfo.mNumberCount; i++) {
       if (aName == *numInfo.mNumberInfo[i].mName) {
         numInfo.Reset(i);
-        DidChangeNumber(i, false);
         return;
       }
     }
@@ -647,8 +710,8 @@ nsSVGElement::UnsetAttrInternal(PRInt32 aNamespaceID, nsIAtom* aName,
 
     for (PRUint32 i = 0; i < numPairInfo.mNumberPairCount; i++) {
       if (aName == *numPairInfo.mNumberPairInfo[i].mName) {
+        MaybeSerializeAttrBeforeRemoval(aName, aNotify);
         numPairInfo.Reset(i);
-        DidChangeNumberPair(i, false);
         return;
       }
     }
@@ -659,7 +722,6 @@ nsSVGElement::UnsetAttrInternal(PRInt32 aNamespaceID, nsIAtom* aName,
     for (PRUint32 i = 0; i < intInfo.mIntegerCount; i++) {
       if (aName == *intInfo.mIntegerInfo[i].mName) {
         intInfo.Reset(i);
-        DidChangeInteger(i, false);
         return;
       }
     }
@@ -669,8 +731,8 @@ nsSVGElement::UnsetAttrInternal(PRInt32 aNamespaceID, nsIAtom* aName,
 
     for (PRUint32 i = 0; i < intPairInfo.mIntegerPairCount; i++) {
       if (aName == *intPairInfo.mIntegerPairInfo[i].mName) {
+        MaybeSerializeAttrBeforeRemoval(aName, aNotify);
         intPairInfo.Reset(i);
-        DidChangeIntegerPair(i, false);
         return;
       }
     }
@@ -680,8 +742,8 @@ nsSVGElement::UnsetAttrInternal(PRInt32 aNamespaceID, nsIAtom* aName,
 
     for (PRUint32 i = 0; i < angleInfo.mAngleCount; i++) {
       if (aName == *angleInfo.mAngleInfo[i].mName) {
+        MaybeSerializeAttrBeforeRemoval(aName, aNotify);
         angleInfo.Reset(i);
-        DidChangeAngle(i, false);
         return;
       }
     }
@@ -692,7 +754,6 @@ nsSVGElement::UnsetAttrInternal(PRInt32 aNamespaceID, nsIAtom* aName,
     for (PRUint32 i = 0; i < boolInfo.mBooleanCount; i++) {
       if (aName == *boolInfo.mBooleanInfo[i].mName) {
         boolInfo.Reset(i);
-        DidChangeBoolean(i, false);
         return;
       }
     }
@@ -703,7 +764,6 @@ nsSVGElement::UnsetAttrInternal(PRInt32 aNamespaceID, nsIAtom* aName,
     for (PRUint32 i = 0; i < enumInfo.mEnumCount; i++) {
       if (aName == *enumInfo.mEnumInfo[i].mName) {
         enumInfo.Reset(i);
-        DidChangeEnum(i, false);
         return;
       }
     }
@@ -712,8 +772,8 @@ nsSVGElement::UnsetAttrInternal(PRInt32 aNamespaceID, nsIAtom* aName,
     if (aName == nsGkAtoms::viewBox) {
       nsSVGViewBox* viewBox = GetViewBox();
       if (viewBox) {
+        MaybeSerializeAttrBeforeRemoval(aName, aNotify);
         viewBox->Init();
-        DidChangeViewBox(false);
         return;
       }
     }
@@ -722,10 +782,9 @@ nsSVGElement::UnsetAttrInternal(PRInt32 aNamespaceID, nsIAtom* aName,
     if (aName == nsGkAtoms::preserveAspectRatio) {
       SVGAnimatedPreserveAspectRatio *preserveAspectRatio =
         GetPreserveAspectRatio();
-
       if (preserveAspectRatio) {
+        MaybeSerializeAttrBeforeRemoval(aName, aNotify);
         preserveAspectRatio->Init();
-        DidChangePreserveAspectRatio(false);
         return;
       }
     }
@@ -734,8 +793,8 @@ nsSVGElement::UnsetAttrInternal(PRInt32 aNamespaceID, nsIAtom* aName,
     if (GetTransformListAttrName() == aName) {
       SVGAnimatedTransformList *transformList = GetAnimatedTransformList();
       if (transformList) {
+        MaybeSerializeAttrBeforeRemoval(aName, aNotify);
         transformList->ClearBaseValue();
-        DidChangeTransformList(false);
         return;
       }
     }
@@ -743,6 +802,7 @@ nsSVGElement::UnsetAttrInternal(PRInt32 aNamespaceID, nsIAtom* aName,
     // Check for conditional processing attributes
     nsCOMPtr<DOMSVGTests> tests(do_QueryInterface(this));
     if (tests && tests->IsConditionalProcessingAttribute(aName)) {
+      MaybeSerializeAttrBeforeRemoval(aName, aNotify);
       tests->UnsetAttr(aName);
       return;
     }
@@ -752,6 +812,7 @@ nsSVGElement::UnsetAttrInternal(PRInt32 aNamespaceID, nsIAtom* aName,
 
     for (PRUint32 i = 0; i < stringListInfo.mStringListCount; i++) {
       if (aName == *stringListInfo.mStringListInfo[i].mName) {
+        MaybeSerializeAttrBeforeRemoval(aName, aNotify);
         stringListInfo.Reset(i);
         return;
       }
@@ -765,7 +826,6 @@ nsSVGElement::UnsetAttrInternal(PRInt32 aNamespaceID, nsIAtom* aName,
     if (aNamespaceID == stringInfo.mStringInfo[i].mNamespaceID &&
         aName == *stringInfo.mStringInfo[i].mName) {
       stringInfo.Reset(i);
-      DidChangeString(i);
       return;
     }
   }
@@ -1247,6 +1307,137 @@ nsSVGElement::GetAnimatedContentStyleRule()
                                              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)
 {
@@ -1329,25 +1520,27 @@ nsSVGElement::SetLength(nsIAtom* aName, const nsSVGLength2 &aLength)
   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
@@ -1424,24 +1617,27 @@ nsSVGElement::LengthListAttributesInfo::Reset(PRUint8 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
@@ -1506,24 +1702,28 @@ nsSVGElement::NumberListAttributesInfo::Reset(PRUint8 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
@@ -1565,20 +1765,24 @@ nsSVGElement::GetAnimatedNumberList(nsIAtom *aAttrName)
   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
@@ -1596,18 +1800,24 @@ nsSVGElement::DidAnimatePointList()
   }
 }
 
-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
@@ -1638,22 +1848,17 @@ void nsSVGElement::NumberAttributesInfo::Reset(PRUint8 aAttrEnum)
 }
 
 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);
 }
@@ -1705,25 +1910,27 @@ void nsSVGElement::NumberPairAttributesInfo::Reset(PRUint8 aAttrEnum)
                                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
@@ -1752,22 +1959,17 @@ void nsSVGElement::IntegerAttributesInfo::Reset(PRUint8 aAttrEnum)
 }
 
 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);
 }
@@ -1819,25 +2021,28 @@ void nsSVGElement::IntegerPairAttributesInfo::Reset(PRUint8 aAttrEnum)
                                 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
@@ -1866,25 +2071,26 @@ void nsSVGElement::AngleAttributesInfo::Reset(PRUint8 aAttrEnum)
                           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
@@ -1913,22 +2119,15 @@ void nsSVGElement::BooleanAttributesInfo::Reset(PRUint8 aAttrEnum)
 }
 
 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);
 }
@@ -1959,22 +2158,15 @@ void nsSVGElement::EnumAttributesInfo::Reset(PRUint8 aAttrEnum)
 }
 
 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);
 }
@@ -1998,22 +2190,23 @@ 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
@@ -2034,24 +2227,26 @@ 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
@@ -2066,22 +2261,22 @@ nsSVGElement::DidAnimatePreserveAspectRatio()
   }
 }
 
-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
@@ -2155,29 +2350,49 @@ nsSVGElement::GetStringListInfo()
   return StringListAttributesInfo(nsnull, nsnull, 0);
 }
 
-void
-nsSVGElement::DidChangeStringList(bool aIsConditionalProcessingAttribute,
-                                  PRUint8 aAttrEnum)
+nsAttrValue
+nsSVGElement::WillChangeStringList(bool aIsConditionalProcessingAttribute,
+                                   PRUint8 aAttrEnum)
 {
+  nsIAtom* name;
   if (aIsConditionalProcessingAttribute) {
     nsCOMPtr<DOMSVGTests> tests(do_QueryInterface(this));
-    tests->DidChangeStringList(aAttrEnum);
-    return;
+    name = tests->GetAttrName(aAttrEnum);
+  } else {
+    name = *GetStringListInfo().mStringListInfo[aAttrEnum].mName;
+  }
+  return WillChangeValue(name);
+}
+
+void
+nsSVGElement::DidChangeStringList(bool aIsConditionalProcessingAttribute,
+                                  PRUint8 aAttrEnum,
+                                  const nsAttrValue& aEmptyOrOldValue)
+{
+  nsIAtom* name;
+  nsAttrValue newValue;
+  nsCOMPtr<DOMSVGTests> tests;
+
+  if (aIsConditionalProcessingAttribute) {
+    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();
+  DidChangeValue(name, aEmptyOrOldValue, newValue);
 
-  NS_ASSERTION(info.mStringListCount > 0,
-               "DidChangeStringList on element with no string list attribs");
-
-  NS_ASSERTION(aAttrEnum < info.mStringListCount, "aAttrEnum out of range");
-
-  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
diff --git a/content/svg/content/src/nsSVGElement.h b/content/svg/content/src/nsSVGElement.h
index 53c9c4914bf0..92e1a451274e 100644
--- a/content/svg/content/src/nsSVGElement.h
+++ b/content/svg/content/src/nsSVGElement.h
@@ -169,24 +169,44 @@ public:
     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);
@@ -249,6 +269,16 @@ public:
   }
 
 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,
@@ -264,6 +294,11 @@ protected:
   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 {
diff --git a/content/svg/content/src/nsSVGEnum.cpp b/content/svg/content/src/nsSVGEnum.cpp
index 696c436b3f84..3e5e5317dc65 100644
--- a/content/svg/content/src/nsSVGEnum.cpp
+++ b/content/svg/content/src/nsSVGEnum.cpp
@@ -67,15 +67,12 @@ nsSVGEnum::GetMapping(nsSVGElement *aSVGElement)
 }
 
 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;
@@ -99,19 +96,19 @@ nsSVGEnum::SetBaseValueString(const nsAString& aValue,
   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
@@ -131,7 +128,7 @@ nsSVGEnum::SetBaseValue(PRUint16 aValue,
         else {
           aSVGElement->AnimationNeedsResample();
         }
-        aSVGElement->DidChangeEnum(mAttrEnum, true);
+        aSVGElement->DidChangeEnum(mAttrEnum);
       }
       return NS_OK;
     }
diff --git a/content/svg/content/src/nsSVGEnum.h b/content/svg/content/src/nsSVGEnum.h
index c59cd36d9e66..502537675544 100644
--- a/content/svg/content/src/nsSVGEnum.h
+++ b/content/svg/content/src/nsSVGEnum.h
@@ -65,11 +65,8 @@ public:
     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
diff --git a/content/svg/content/src/nsSVGInteger.cpp b/content/svg/content/src/nsSVGInteger.cpp
index dced1e93840c..8a1f06dbb0d6 100644
--- a/content/svg/content/src/nsSVGInteger.cpp
+++ b/content/svg/content/src/nsSVGInteger.cpp
@@ -106,9 +106,16 @@ nsSVGInteger::GetBaseValueString(nsAString & aValueAsString)
 }
 
 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) {
@@ -117,7 +124,7 @@ nsSVGInteger::SetBaseValue(int aValue,
   else {
     aSVGElement->AnimationNeedsResample();
   }
-  aSVGElement->DidChangeInteger(mAttrEnum, true);
+  aSVGElement->DidChangeInteger(mAttrEnum);
 }
 
 void
diff --git a/content/svg/content/src/nsSVGIntegerPair.cpp b/content/svg/content/src/nsSVGIntegerPair.cpp
index b5f5d8b85960..dc35ada0e4ed 100644
--- a/content/svg/content/src/nsSVGIntegerPair.cpp
+++ b/content/svg/content/src/nsSVGIntegerPair.cpp
@@ -128,7 +128,7 @@ nsSVGIntegerPair::SetBaseValueString(const nsAString &aValueAsString,
 }
 
 void
-nsSVGIntegerPair::GetBaseValueString(nsAString &aValueAsString)
+nsSVGIntegerPair::GetBaseValueString(nsAString &aValueAsString) const
 {
   aValueAsString.Truncate();
   aValueAsString.AppendInt(mBaseVal[0]);
@@ -143,6 +143,11 @@ 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) {
@@ -151,13 +156,18 @@ nsSVGIntegerPair::SetBaseValue(PRInt32 aValue, PairIndex aPairIndex,
   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;
@@ -168,7 +178,7 @@ nsSVGIntegerPair::SetBaseValues(PRInt32 aValue1, PRInt32 aValue2,
   else {
     aSVGElement->AnimationNeedsResample();
   }
-  aSVGElement->DidChangeIntegerPair(mAttrEnum, true);
+  aSVGElement->DidChangeIntegerPair(mAttrEnum, emptyOrOldValue);
 }
 
 void
diff --git a/content/svg/content/src/nsSVGIntegerPair.h b/content/svg/content/src/nsSVGIntegerPair.h
index 63ae54412167..2e181615b958 100644
--- a/content/svg/content/src/nsSVGIntegerPair.h
+++ b/content/svg/content/src/nsSVGIntegerPair.h
@@ -66,7 +66,7 @@ public:
 
   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);
diff --git a/content/svg/content/src/nsSVGLength2.cpp b/content/svg/content/src/nsSVGLength2.cpp
index 4709ae0e8ba0..40301209dc08 100644
--- a/content/svg/content/src/nsSVGLength2.cpp
+++ b/content/svg/content/src/nsSVGLength2.cpp
@@ -307,8 +307,17 @@ nsSVGLength2::GetUnitScaleFactor(nsIFrame *aFrame, PRUint8 aUnitType) const
 
 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) {
@@ -317,7 +326,9 @@ nsSVGLength2::SetBaseValueInSpecifiedUnits(float aValue,
   else {
     aSVGElement->AnimationNeedsResample();
   }
-  aSVGElement->DidChangeLength(mAttrEnum, true);
+  if (aDoSetAttr) {
+    aSVGElement->DidChangeLength(mAttrEnum, emptyOrOldValue);
+  }
 }
 
 nsresult
@@ -327,10 +338,23 @@ nsSVGLength2::ConvertToSpecifiedUnits(PRUint16 unitType,
   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;
 }
@@ -345,6 +369,12 @@ nsSVGLength2::NewValueSpecifiedUnits(PRUint16 unitType,
   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);
@@ -354,7 +384,7 @@ nsSVGLength2::NewValueSpecifiedUnits(PRUint16 unitType,
   else {
     aSVGElement->AnimationNeedsResample();
   }
-  aSVGElement->DidChangeLength(mAttrEnum, true);
+  aSVGElement->DidChangeLength(mAttrEnum, emptyOrOldValue);
   return NS_OK;
 }
 
@@ -407,12 +437,21 @@ nsSVGLength2::SetBaseValueString(const nsAString &aValueAsString,
 {
   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);
@@ -423,28 +462,31 @@ nsSVGLength2::SetBaseValueString(const nsAString &aValueAsString,
     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
diff --git a/content/svg/content/src/nsSVGLength2.h b/content/svg/content/src/nsSVGLength2.h
index 1882ef2ec7fc..1d555b0826bf 100644
--- a/content/svg/content/src/nsSVGLength2.h
+++ b/content/svg/content/src/nsSVGLength2.h
@@ -82,8 +82,8 @@ public:
   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); }
@@ -148,8 +148,9 @@ private:
   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,
@@ -180,7 +181,7 @@ private:
         if (!NS_finite(aValue)) {
           return NS_ERROR_ILLEGAL_VALUE;
         }
-        mVal->SetBaseValue(aValue, mSVGElement);
+        mVal->SetBaseValue(aValue, mSVGElement, true);
         return NS_OK;
       }
 
@@ -191,7 +192,7 @@ private:
         if (!NS_finite(aValue)) {
           return NS_ERROR_ILLEGAL_VALUE;
         }
-        mVal->SetBaseValueInSpecifiedUnits(aValue, mSVGElement);
+        mVal->SetBaseValueInSpecifiedUnits(aValue, mSVGElement, true);
         return NS_OK;
       }
 
diff --git a/content/svg/content/src/nsSVGMarkerElement.cpp b/content/svg/content/src/nsSVGMarkerElement.cpp
index e16500449b4f..91c6e2950fc0 100644
--- a/content/svg/content/src/nsSVGMarkerElement.cpp
+++ b/content/svg/content/src/nsSVGMarkerElement.cpp
@@ -224,7 +224,7 @@ NS_IMETHODIMP nsSVGMarkerElement::SetOrientToAngle(nsIDOMSVGAngle *angle)
   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;
 }
diff --git a/content/svg/content/src/nsSVGNumber2.cpp b/content/svg/content/src/nsSVGNumber2.cpp
index 5a0450c132e8..9cc4d64632a2 100644
--- a/content/svg/content/src/nsSVGNumber2.cpp
+++ b/content/svg/content/src/nsSVGNumber2.cpp
@@ -149,9 +149,12 @@ nsSVGNumber2::GetBaseValueString(nsAString & aValueAsString)
 }
 
 void
-nsSVGNumber2::SetBaseValue(float aValue,
-                           nsSVGElement *aSVGElement)
+nsSVGNumber2::SetBaseValue(float aValue, nsSVGElement *aSVGElement)
 {
+  if (mIsBaseSet && aValue == mBaseVal) {
+    return;
+  }
+
   mBaseVal = aValue;
   mIsBaseSet = true;
   if (!mIsAnimated) {
@@ -160,7 +163,7 @@ nsSVGNumber2::SetBaseValue(float aValue,
   else {
     aSVGElement->AnimationNeedsResample();
   }
-  aSVGElement->DidChangeNumber(mAttrEnum, true);
+  aSVGElement->DidChangeNumber(mAttrEnum);
 }
 
 void
diff --git a/content/svg/content/src/nsSVGNumberPair.cpp b/content/svg/content/src/nsSVGNumberPair.cpp
index 38254fbfcd0c..825704e0476d 100644
--- a/content/svg/content/src/nsSVGNumberPair.cpp
+++ b/content/svg/content/src/nsSVGNumberPair.cpp
@@ -121,14 +121,14 @@ nsSVGNumberPair::SetBaseValueString(const nsAString &aValueAsString,
     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]);
@@ -143,6 +143,10 @@ 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) {
@@ -151,13 +155,17 @@ nsSVGNumberPair::SetBaseValue(float aValue, PairIndex aPairIndex,
   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;
@@ -168,7 +176,7 @@ nsSVGNumberPair::SetBaseValues(float aValue1, float aValue2,
   else {
     aSVGElement->AnimationNeedsResample();
   }
-  aSVGElement->DidChangeNumberPair(mAttrEnum, true);
+  aSVGElement->DidChangeNumberPair(mAttrEnum, emptyOrOldValue);
 }
 
 void
diff --git a/content/svg/content/src/nsSVGNumberPair.h b/content/svg/content/src/nsSVGNumberPair.h
index e520ec523cad..f6987724c870 100644
--- a/content/svg/content/src/nsSVGNumberPair.h
+++ b/content/svg/content/src/nsSVGNumberPair.h
@@ -67,7 +67,7 @@ public:
 
   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);
diff --git a/content/svg/content/src/nsSVGViewBox.cpp b/content/svg/content/src/nsSVGViewBox.cpp
index 3c34bb5fa417..3d29219439fb 100644
--- a/content/svg/content/src/nsSVGViewBox.cpp
+++ b/content/svg/content/src/nsSVGViewBox.cpp
@@ -127,10 +127,16 @@ 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();
   }
@@ -185,7 +191,7 @@ nsSVGViewBox::SetBaseValueString(const nsAString& aValue,
     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.
   }
diff --git a/content/svg/content/test/Makefile.in b/content/svg/content/test/Makefile.in
index 0925d9de0b03..5bcc478ca3bc 100644
--- a/content/svg/content/test/Makefile.in
+++ b/content/svg/content/test/Makefile.in
@@ -50,6 +50,7 @@ include $(topsrcdir)/config/rules.mk
 
 _TEST_FILES = \
 		matrixUtils.js \
+		MutationEventChecker.js \
 		test_a_href_01.xhtml \
 		test_a_href_02.xhtml \
 		a_href_destination.svg \
@@ -65,6 +66,7 @@ _TEST_FILES = \
 		bbox-helper.svg \
 		bounds-helper.svg \
 		test_dataTypes.html \
+		test_dataTypesModEvents.html \
 		dataTypes-helper.svg \
 		getCTM-helper.svg \
 		test_getCTM.html \
@@ -88,7 +90,9 @@ _TEST_FILES = \
 		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 \
diff --git a/content/svg/content/test/MutationEventChecker.js b/content/svg/content/test/MutationEventChecker.js
new file mode 100644
index 000000000000..1394b6ca1640
--- /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;
+    }
+  }
+}
diff --git a/content/svg/content/test/test_SVGLengthList.xhtml b/content/svg/content/test/test_SVGLengthList.xhtml
index 876421df3f60..fe96cb2c2054 100644
--- a/content/svg/content/test/test_SVGLengthList.xhtml
+++ b/content/svg/content/test/test_SVGLengthList.xhtml
@@ -5,6 +5,7 @@ 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>
@@ -36,11 +37,31 @@ function run_tests()
 
   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();
 }
diff --git a/content/svg/content/test/test_SVGNumberList.xhtml b/content/svg/content/test/test_SVGNumberList.xhtml
new file mode 100644
index 000000000000..fcd471f4c4f2
--- /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>
diff --git a/content/svg/content/test/test_SVGPathSegList.xhtml b/content/svg/content/test/test_SVGPathSegList.xhtml
index 73513197f4f9..b1c42bc1e8b2 100644
--- a/content/svg/content/test/test_SVGPathSegList.xhtml
+++ b/content/svg/content/test/test_SVGPathSegList.xhtml
@@ -5,6 +5,7 @@ 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>
@@ -86,17 +87,25 @@ function run_tests()
   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();
 }
diff --git a/content/svg/content/test/test_SVGPointList.xhtml b/content/svg/content/test/test_SVGPointList.xhtml
new file mode 100644
index 000000000000..fbeaacf77a53
--- /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>
diff --git a/content/svg/content/test/test_SVGTransformList.xhtml b/content/svg/content/test/test_SVGTransformList.xhtml
index 00793cc2169b..453f1efefb16 100644
--- a/content/svg/content/test/test_SVGTransformList.xhtml
+++ b/content/svg/content/test/test_SVGTransformList.xhtml
@@ -7,6 +7,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=602759
   <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>
@@ -41,7 +42,8 @@ function main()
       testCreateSVGTransformFromMatrix,
       testReadOnly,
       testOrphan,
-      testFailedSet
+      testFailedSet,
+      testMutationEvents
     ];
   for (var i = 0; i < tests.length; i++) {
     tests[i](g);
@@ -355,6 +357,76 @@ function testFailedSet(g)
      "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);
 
 ]]>
diff --git a/content/svg/content/test/test_SVGxxxList.xhtml b/content/svg/content/test/test_SVGxxxList.xhtml
index e13171b968b5..08286d487ff1 100644
--- a/content/svg/content/test/test_SVGxxxList.xhtml
+++ b/content/svg/content/test/test_SVGxxxList.xhtml
@@ -6,6 +6,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=515116
   <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>
@@ -452,6 +453,7 @@ function get_array_of_list_items(list)
 function run_baseVal_API_tests()
 {
   var res, threw, items;
+  var eventChecker = new MutationEventChecker;
 
   for each (var t in tests) {
 
@@ -462,6 +464,8 @@ function run_baseVal_API_tests()
     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,
@@ -469,13 +473,48 @@ function run_baseVal_API_tests()
        ' 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.');
@@ -515,6 +554,7 @@ function run_baseVal_API_tests()
          '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({});
@@ -524,6 +564,7 @@ function run_baseVal_API_tests()
       ok(threw,
          'The method '+t.list_type+'.initialize() should throw if passed an '+
          'object of the wrong type.');
+      eventChecker.ignoreEvents();
     }
 
     // Test .insertItemBefore():
@@ -532,7 +573,9 @@ function run_baseVal_API_tests()
 
     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.');
@@ -587,6 +630,7 @@ function run_baseVal_API_tests()
          'the list at the index specified.');
       // [SVGWG issue] not what the spec currently says
 
+      eventChecker.expect("");
       threw = false;
       try {
         t.baseVal.insertItemBefore({}, 2);
@@ -596,6 +640,7 @@ function run_baseVal_API_tests()
       ok(threw,
          'The method '+t.list_type+'.insertItemBefore() should throw if passed '+
          'an object of the wrong type.');
+      eventChecker.ignoreEvents();
     }
 
     // Test .replaceItem():
@@ -604,7 +649,9 @@ function run_baseVal_API_tests()
 
     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.');
@@ -627,6 +674,7 @@ function run_baseVal_API_tests()
 
     item = t.item_constructor();
 
+    eventChecker.expect("");
     threw = false;
     try {
       t.baseVal.replaceItem(item, 100);
@@ -636,6 +684,7 @@ function run_baseVal_API_tests()
     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);
@@ -683,7 +732,9 @@ function run_baseVal_API_tests()
 
     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.');
@@ -701,6 +752,7 @@ function run_baseVal_API_tests()
        'the item at index 2 was removed using the '+t.list_type+
        '.replaceItem() method.');
 
+    eventChecker.expect("");
     threw = false;
     try {
       t.baseVal.removeItem(100);
@@ -710,6 +762,7 @@ function run_baseVal_API_tests()
     ok(threw,
        'The method '+t.list_type+'.removeItem() should throw if passed '+
        'an index that is out of bounds.');
+    eventChecker.ignoreEvents();
 
     // Test .appendItem():
 
@@ -717,7 +770,9 @@ function run_baseVal_API_tests()
 
     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.');
@@ -763,6 +818,7 @@ function run_baseVal_API_tests()
          'that list.');
       // [SVGWG issue] not what the spec currently says
 
+      eventChecker.expect("");
       threw = false;
       try {
         t.baseVal.appendItem({});
@@ -772,7 +828,15 @@ function run_baseVal_API_tests()
       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();
   }
 }
 
@@ -880,7 +944,7 @@ function run_animVal_API_tests()
 
 /**
  * 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()
 {
diff --git a/content/svg/content/test/test_dataTypes.html b/content/svg/content/test/test_dataTypes.html
index 75930af3020b..a6035cfdd713 100644
--- a/content/svg/content/test/test_dataTypes.html
+++ b/content/svg/content/test/test_dataTypes.html
@@ -38,8 +38,11 @@ function runTests()
   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
 
@@ -57,6 +60,11 @@ function runTests()
   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");
@@ -67,6 +75,11 @@ function runTests()
   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");
@@ -89,6 +102,13 @@ function runTests()
   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");
@@ -97,6 +117,10 @@ function runTests()
   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
 
@@ -120,6 +144,13 @@ function runTests()
   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");
@@ -136,6 +167,11 @@ function runTests()
   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");
@@ -144,6 +180,11 @@ function runTests()
   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");
 
   // enum attribute
 
@@ -153,6 +194,10 @@ function runTests()
   convolve.edgeMode.baseVal = 1;
   is(convolve.edgeMode.animVal, 1, "enum animVal");
   is(convolve.getAttribute("edgeMode"), "duplicate", "enum attribute");
+  convolve.setAttribute("edgeMode", "");
+  ok(convolve.getAttribute("edgeMode") === "", "empty enum attribute");
+  convolve.removeAttribute("edgeMode");
+  ok(convolve.getAttribute("edgeMode") === null, "removed enum attribute");
 
   // string attribute
 
@@ -162,6 +207,10 @@ function runTests()
   convolve.result.baseVal = "bar";
   is(convolve.result.animVal, "bar", "string animVal");
   is(convolve.getAttribute("result"), "bar", "string attribute");
+  convolve.setAttribute("result", "");
+  ok(convolve.getAttribute("result") === "", "empty string attribute");
+  convolve.removeAttribute("result");
+  ok(convolve.getAttribute("result") === null, "removed string attribute");
 
   // preserveAspectRatio attribute
 
@@ -186,6 +235,13 @@ function runTests()
   is(basePreserveAspectRatio.meetOrSlice, 2, "preserveAspectRatio.meetOrSlice baseVal");
   is(animPreserveAspectRatio.meetOrSlice, 2, "preserveAspectRatio.meetOrSlice animVal");
 
+  marker.setAttribute("preserveAspectRatio", "");
+  ok(marker.getAttribute("preserveAspectRatio") === "",
+     "empty preserveAspectRatio attribute");
+  marker.removeAttribute("preserveAspectRatio");
+  ok(marker.getAttribute("preserveAspectRatio") === null,
+     "removed preserveAspectRatio attribute");
+
   // viewBox attribute
   var baseViewBox = marker.viewBox.baseVal;
   var animViewBox = marker.viewBox.animVal;
@@ -223,6 +279,10 @@ function runTests()
   is(marker.getAttribute("viewBox"), "5 6 7 8", "viewBox attribute");
   marker.removeAttribute("viewBox");
   is(marker.hasAttribute("viewBox"), false, "viewBox hasAttribute");
+  ok(marker.getAttribute("viewBox") === null, "removed viewBox attribute");
+
+  marker.setAttribute("viewBox", "");
+  ok(marker.getAttribute("viewBox") === "", "empty viewBox attribute");
 
   SimpleTest.finish();
 }
diff --git a/content/svg/content/test/test_dataTypesModEvents.html b/content/svg/content/test/test_dataTypesModEvents.html
new file mode 100644
index 000000000000..e45c6415a618
--- /dev/null
+++ b/content/svg/content/test/test_dataTypesModEvents.html
@@ -0,0 +1,245 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=629200
+-->
+<head>
+  <title>Test for Bug 629200</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/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"></div>
+
+<iframe id="svg" src="dataTypes-helper.svg"></iframe>
+
+<pre id="test">
+<script class="testbody" type="application/javascript">
+SimpleTest.waitForExplicitFinish();
+
+function runTests()
+{
+  var doc = $("svg").contentWindow.document;
+  var filter = doc.getElementById("filter");
+  var convolve = doc.getElementById("convolve");
+  var blur = doc.getElementById("blur");
+  var marker = doc.getElementById("marker");
+  var eventChecker = new MutationEventChecker;
+
+  // class attribute
+ 
+  eventChecker.watchAttr(filter, "class");
+  eventChecker.expect("add modify remove add");
+  filter.setAttribute("class", "foo");
+  filter.className.baseVal = "bar";
+  filter.removeAttribute("class");
+  filter.className.baseVal = "foo";
+
+  eventChecker.expect("");
+  filter.className.baseVal = "foo";
+  filter.setAttribute("class", "foo");
+
+  // length attribute
+
+  eventChecker.watchAttr(marker, "markerWidth");
+  eventChecker.expect("add modify modify modify modify modify remove add");
+  marker.setAttribute("markerWidth", "12.5");
+  marker.markerWidth.baseVal.value = 8;
+  marker.markerWidth.baseVal.valueInSpecifiedUnits = 9;
+  marker.markerWidth.baseVal.valueAsString = "10";
+  marker.markerWidth.baseVal.convertToSpecifiedUnits(
+    SVGLength.SVG_LENGTHTYPE_CM);
+  marker.markerWidth.baseVal.newValueSpecifiedUnits(
+    SVGLength.SVG_LENGTHTYPE_MM, 11);
+  marker.removeAttribute("markerWidth");
+  marker.markerWidth.baseVal.value = 8;
+
+  eventChecker.expect("remove add modify");
+  marker.removeAttribute("markerWidth");
+  console.log(marker.getAttribute("markerWidth"));
+  marker.markerWidth.baseVal.convertToSpecifiedUnits(
+    SVGLength.SVG_LENGTHTYPE_NUMBER);
+  console.log(marker.getAttribute("markerWidth"));
+  marker.markerWidth.baseVal.value = 8;
+
+  eventChecker.expect("");
+  marker.markerWidth.baseVal.value = 8;
+  marker.setAttribute("markerWidth", "8");
+  marker.markerWidth.baseVal.value = 8;
+  marker.markerWidth.baseVal.valueAsString = "8";
+  marker.markerWidth.baseVal.convertToSpecifiedUnits(
+    SVGLength.SVG_LENGTHTYPE_NUMBER);
+  marker.markerWidth.baseVal.newValueSpecifiedUnits(
+    SVGLength.SVG_LENGTHTYPE_NUMBER, 8);
+
+  // number attribute
+
+  eventChecker.watchAttr(convolve, "divisor");
+  eventChecker.expect("add modify remove add");
+  convolve.setAttribute("divisor", "12.5");
+  convolve.divisor.baseVal = 6;
+  convolve.removeAttribute("divisor");
+  convolve.divisor.baseVal = 8;
+
+  eventChecker.expect("");
+  convolve.divisor.baseVal = 8;
+  convolve.setAttribute("divisor", "8");
+
+  // number-optional-number attribute
+
+  eventChecker.watchAttr(blur, "stdDeviation");
+  eventChecker.expect("add modify remove add");
+  blur.setAttribute("stdDeviation", "5, 6");
+  blur.stdDeviationX.baseVal = 8;
+  blur.removeAttribute("stdDeviation");
+  blur.setAttribute("stdDeviation", "2, 3");
+
+  eventChecker.expect("");
+  blur.stdDeviationX.baseVal = 2;
+  blur.stdDeviationY.baseVal = 3;
+  blur.setAttribute("stdDeviation", "2, 3");
+
+  // integer attribute
+
+  eventChecker.watchAttr(convolve, "targetX");
+  eventChecker.expect("add modify remove add");
+  convolve.setAttribute("targetX", "12");
+  convolve.targetX.baseVal = 6;
+  convolve.removeAttribute("targetX");
+  convolve.targetX.baseVal = 8;
+
+  // Check redundant change when comparing typed value to attribute value
+  eventChecker.expect("");
+  convolve.setAttribute("targetX", "8");
+  // Check redundant change when comparing attribute value to typed value
+  eventChecker.expect("remove add");
+  convolve.removeAttribute("targetX");
+  convolve.setAttribute("targetX", "8");
+  convolve.targetX.baseVal = 8;
+
+  // integer-optional-integer attribute
+
+  eventChecker.watchAttr(filter, "filterRes");
+  eventChecker.expect("add modify remove add");
+  filter.setAttribute("filterRes", "60, 70");
+  filter.filterResX.baseVal = 50;
+  filter.removeAttribute("filterRes");
+  filter.setAttribute("filterRes", "50, 60");
+
+  eventChecker.expect("");
+  filter.filterResX.baseVal = 50;
+  filter.setAttribute("filterRes", "50, 60");
+  filter.filterResY.baseVal = 60;
+
+  // angle attribute
+
+  eventChecker.watchAttr(marker, "orient");
+  eventChecker.expect("add modify modify modify modify modify remove add");
+  marker.setAttribute("orient", "90deg");
+  marker.orientAngle.baseVal.value = 12;
+  marker.orientAngle.baseVal.valueInSpecifiedUnits = 23;
+  marker.orientAngle.baseVal.valueAsString = "34";
+  marker.orientAngle.baseVal.newValueSpecifiedUnits(
+    SVGAngle.SVG_ANGLETYPE_GRAD, 34);
+  marker.orientAngle.baseVal.newValueSpecifiedUnits(
+    SVGAngle.SVG_ANGLETYPE_GRAD, 45);
+  marker.removeAttribute("orient");
+  marker.orientAngle.baseVal.value = 40;
+
+  eventChecker.expect("");
+  marker.orientAngle.baseVal.value = 40;
+  marker.orientAngle.baseVal.valueInSpecifiedUnits = 40;
+  marker.orientAngle.baseVal.valueAsString = "40";
+  marker.orientAngle.baseVal.convertToSpecifiedUnits(
+    SVGAngle.SVG_ANGLETYPE_UNSPECIFIED);
+  marker.orientAngle.baseVal.newValueSpecifiedUnits(
+    SVGAngle.SVG_ANGLETYPE_UNSPECIFIED, 40);
+
+  // boolean attribute
+
+  eventChecker.watchAttr(convolve, "preserveAlpha");
+  eventChecker.expect("add modify remove add");
+  convolve.setAttribute("preserveAlpha", "true");
+  convolve.preserveAlpha.baseVal = false;
+  convolve.removeAttribute("preserveAlpha");
+  convolve.preserveAlpha.baseVal = true;
+
+  eventChecker.expect("");
+  convolve.preserveAlpha.baseVal = true;
+  convolve.setAttribute("preserveAlpha", "true");
+
+  // enum attribute
+
+  eventChecker.watchAttr(convolve, "edgeMode");
+  eventChecker.expect("add modify remove add");
+  convolve.setAttribute("edgeMode", "none");
+  convolve.edgeMode.baseVal = SVGFEConvolveMatrixElement.SVG_EDGEMODE_WRAP;
+  convolve.removeAttribute("edgeMode");
+  convolve.edgeMode.baseVal = SVGFEConvolveMatrixElement.SVG_EDGEMODE_NONE;
+
+  eventChecker.expect("");
+  convolve.edgeMode.baseVal = SVGFEConvolveMatrixElement.SVG_EDGEMODE_NONE;
+  convolve.setAttribute("edgeMode", "none");
+  convolve.edgeMode.baseVal = SVGFEConvolveMatrixElement.SVG_EDGEMODE_NONE;
+
+  // string attribute
+
+  eventChecker.watchAttr(convolve, "result");
+  eventChecker.expect("add modify remove add");
+  convolve.setAttribute("result", "bar");
+  convolve.result.baseVal = "foo";
+  convolve.removeAttribute("result");
+  convolve.result.baseVal = "bar";
+
+  eventChecker.expect("");
+  convolve.result.baseVal = "bar";
+  convolve.setAttribute("result", "bar");
+  convolve.result.baseVal = "bar";
+
+  // preserveAspectRatio attribute
+
+  eventChecker.watchAttr(marker, "preserveAspectRatio");
+  eventChecker.expect("add modify remove add");
+  marker.setAttribute("preserveAspectRatio", "xMaxYMid slice");
+  marker.preserveAspectRatio.baseVal.align =
+    SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMAXYMAX;
+  marker.removeAttribute("preserveAspectRatio");
+  marker.preserveAspectRatio.baseVal.align =
+    SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMIDYMIN;
+
+  eventChecker.expect("");
+  marker.preserveAspectRatio.baseVal.meetOrSlice =
+    SVGPreserveAspectRatio.SVG_MEETORSLICE_MEET;
+  marker.setAttribute("preserveAspectRatio", "xMidYMin meet");
+
+  // viewBox attribute
+
+  eventChecker.watchAttr(marker, "viewBox");
+  eventChecker.expect("add modify remove add");
+  marker.setAttribute("viewBox", "1 2 3 4");
+  marker.viewBox.baseVal.height = 5;
+  marker.removeAttribute("viewBox");
+  marker.viewBox.baseVal.height = 4;
+
+  eventChecker.ignoreEvents();
+  marker.setAttribute("viewBox", "1 2 3 4");
+  eventChecker.expect("");
+  marker.viewBox.baseVal.height = 4;
+  marker.viewBox.baseVal.x = 1;
+  marker.setAttribute("viewBox", "1 2 3 4");
+
+  eventChecker.finish();
+
+  SimpleTest.finish();
+}
+
+window.addEventListener("load", runTests, false);
+</script>
+</pre>
+</body>
+</html>
diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp
index 7faa360321ac..6fd6c8cf816e 100644
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -4121,7 +4121,7 @@ nsDocShell::DisplayLoadError(nsresult aError, nsIURI *aURI,
     NS_ENSURE_FALSE(messageStr.IsEmpty(), NS_ERROR_FAILURE);
     // Note: For now, display an alert instead of an error page if we have no
     // URI object. Missing URI objects are handled badly by session history.
-    if (mUseErrorPages && aURI && aFailedChannel) {
+    if (mUseErrorPages && aURI) {
         // Display an error page
         LoadErrorPage(aURI, aURL, errorPage.get(), error.get(),
                       messageStr.get(), cssClass.get(), aFailedChannel);
diff --git a/dom/Makefile.in b/dom/Makefile.in
index d082f8d51756..91a0480f65e5 100644
--- a/dom/Makefile.in
+++ b/dom/Makefile.in
@@ -97,6 +97,11 @@ DIRS += \
   $(NULL)
 endif
 
+ifdef MOZ_B2G_BT
+DIRS += \
+  bluetooth \
+  $(NULL)
+endif
 TEST_DIRS += tests
 ifneq (,$(filter gtk2 cocoa windows android qt os2,$(MOZ_WIDGET_TOOLKIT)))
 TEST_DIRS += plugins/test
diff --git a/dom/base/Navigator.cpp b/dom/base/Navigator.cpp
index e9890da74f07..d032c2986fe1 100644
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -80,6 +80,10 @@
 #ifdef MOZ_B2G_RIL
 #include "TelephonyFactory.h"
 #endif
+#ifdef MOZ_B2G_BT
+#include "nsIDOMBluetoothAdapter.h"
+#include "BluetoothAdapter.h"
+#endif
 
 // This should not be in the namespace.
 DOMCI_DATA(Navigator, mozilla::dom::Navigator)
@@ -133,6 +137,9 @@ NS_INTERFACE_MAP_BEGIN(Navigator)
   NS_INTERFACE_MAP_ENTRY(nsIDOMNavigatorTelephony)
 #endif
   NS_INTERFACE_MAP_ENTRY(nsIDOMMozNavigatorNetwork)
+#ifdef MOZ_B2G_BT
+  NS_INTERFACE_MAP_ENTRY(nsIDOMNavigatorBluetooth)
+#endif
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(Navigator)
 NS_INTERFACE_MAP_END
 
@@ -182,6 +189,12 @@ Navigator::Invalidate()
     mConnection->Shutdown();
     mConnection = nsnull;
   }
+
+#ifdef MOZ_B2G_BT
+  if (mBluetooth) {
+    mBluetooth = nsnull;
+  }
+#endif
 }
 
 nsPIDOMWindow *
@@ -1112,6 +1125,30 @@ Navigator::GetMozConnection(nsIDOMMozConnection** aConnection)
   return NS_OK;
 }
 
+#ifdef MOZ_B2G_BT
+//*****************************************************************************
+//    nsNavigator::nsIDOMNavigatorBluetooth
+//*****************************************************************************
+
+NS_IMETHODIMP
+Navigator::GetMozBluetooth(nsIDOMBluetoothAdapter** aBluetooth)
+{
+  nsCOMPtr<nsIDOMBluetoothAdapter> bluetooth = mBluetooth;
+
+  if (!bluetooth) {
+    nsCOMPtr<nsPIDOMWindow> window = do_QueryReferent(mWindow);
+    NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
+
+    mBluetooth = new bluetooth::BluetoothAdapter();
+
+    bluetooth = mBluetooth;
+  }
+
+  bluetooth.forget(aBluetooth);
+  return NS_OK;
+}
+#endif //MOZ_B2G_BT
+
 PRInt64
 Navigator::SizeOf() const
 {
diff --git a/dom/base/Navigator.h b/dom/base/Navigator.h
index 35dc43dfa05f..3ba648c7a1ab 100644
--- a/dom/base/Navigator.h
+++ b/dom/base/Navigator.h
@@ -65,6 +65,11 @@ class nsIDOMMozConnection;
 class nsIDOMTelephony;
 #endif
 
+#ifdef MOZ_B2G_BT
+#include "nsIDOMNavigatorBluetooth.h"
+#endif
+
+class nsIDOMAdapter;
 //*****************************************************************************
 // Navigator: Script "navigator" object
 //*****************************************************************************
@@ -98,6 +103,9 @@ class Navigator : public nsIDOMNavigator
                 , public nsIDOMNavigatorTelephony
 #endif
                 , public nsIDOMMozNavigatorNetwork
+#ifdef MOZ_B2G_BT
+                , public nsIDOMNavigatorBluetooth
+#endif
 {
 public:
   Navigator(nsPIDOMWindow *aInnerWindow);
@@ -115,6 +123,10 @@ public:
 #endif
   NS_DECL_NSIDOMMOZNAVIGATORNETWORK
 
+#ifdef MOZ_B2G_BT
+  NS_DECL_NSIDOMNAVIGATORBLUETOOTH
+#endif
+
   static void Init();
 
   void Invalidate();
@@ -146,6 +158,9 @@ private:
   nsCOMPtr<nsIDOMTelephony> mTelephony;
 #endif
   nsRefPtr<network::Connection> mConnection;
+#ifdef MOZ_B2G_BT
+  nsCOMPtr<nsIDOMBluetoothAdapter> mBluetooth;
+#endif
   nsWeakPtr mWindow;
 };
 
diff --git a/dom/base/nsDOMClassInfo.cpp b/dom/base/nsDOMClassInfo.cpp
index 2ef36d34bd8a..efe13341ef79 100644
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -532,6 +532,10 @@ using mozilla::dom::indexedDB::IDBWrapperCache;
 #include "CallEvent.h"
 #endif
 
+#ifdef MOZ_B2G_BT
+#include "BluetoothAdapter.h"
+#endif
+
 #include "DOMError.h"
 
 using namespace mozilla;
@@ -1627,6 +1631,11 @@ static nsDOMClassInfoData sClassInfoData[] = {
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 #endif
 
+#ifdef MOZ_B2G_BT
+  NS_DEFINE_CLASSINFO_DATA(BluetoothAdapter, nsDOMGenericSH,
+                           DOM_DEFAULT_SCRIPTABLE_FLAGS)
+#endif
+
   NS_DEFINE_CLASSINFO_DATA(DOMError, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 };
@@ -2423,6 +2432,9 @@ nsDOMClassInfo::Init()
 #endif
     DOM_CLASSINFO_MAP_CONDITIONAL_ENTRY(nsIDOMMozNavigatorNetwork,
                                         network::IsAPIEnabled())
+#ifdef MOZ_B2G_BT
+    DOM_CLASSINFO_MAP_ENTRY(nsIDOMNavigatorBluetooth)
+#endif
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(Plugin, nsIDOMPlugin)
@@ -4365,6 +4377,12 @@ nsDOMClassInfo::Init()
   DOM_CLASSINFO_MAP_END
 #endif
 
+#ifdef MOZ_B2G_BT
+  DOM_CLASSINFO_MAP_BEGIN(BluetoothAdapter, nsIDOMBluetoothAdapter)
+    DOM_CLASSINFO_MAP_ENTRY(nsIDOMBluetoothAdapter)
+  DOM_CLASSINFO_MAP_END
+#endif
+
   DOM_CLASSINFO_MAP_BEGIN(DOMError, nsIDOMDOMError)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMDOMError)
   DOM_CLASSINFO_MAP_END
diff --git a/dom/base/nsDOMClassInfoClasses.h b/dom/base/nsDOMClassInfoClasses.h
index 8a945a897d1d..2c3aeed980ce 100644
--- a/dom/base/nsDOMClassInfoClasses.h
+++ b/dom/base/nsDOMClassInfoClasses.h
@@ -540,4 +540,8 @@ DOMCI_CLASS(TelephonyCall)
 DOMCI_CLASS(CallEvent)
 #endif
 
+#ifdef MOZ_B2G_BT
+DOMCI_CLASS(BluetoothAdapter)
+#endif
+
 DOMCI_CLASS(DOMError)
diff --git a/dom/bluetooth/BluetoothAdapter.cpp b/dom/bluetooth/BluetoothAdapter.cpp
new file mode 100644
index 000000000000..80faefc2a4a8
--- /dev/null
+++ b/dom/bluetooth/BluetoothAdapter.cpp
@@ -0,0 +1,39 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=40: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "BluetoothAdapter.h"
+#include "nsDOMClassInfo.h"
+
+USING_BLUETOOTH_NAMESPACE
+
+BluetoothAdapter::BluetoothAdapter() : mPower(false)
+{
+}
+
+NS_INTERFACE_MAP_BEGIN(BluetoothAdapter)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMBluetoothAdapter)
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(BluetoothAdapter)
+NS_INTERFACE_MAP_END
+  
+NS_IMPL_ADDREF(BluetoothAdapter)
+NS_IMPL_RELEASE(BluetoothAdapter)
+
+DOMCI_DATA(BluetoothAdapter, BluetoothAdapter)
+  
+NS_IMETHODIMP
+BluetoothAdapter::GetPower(bool* aPower)
+{
+  *aPower = mPower;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+BluetoothAdapter::SetPower(bool aPower)
+{
+  mPower = aPower;
+  return NS_OK;
+}
diff --git a/dom/bluetooth/BluetoothAdapter.h b/dom/bluetooth/BluetoothAdapter.h
new file mode 100644
index 000000000000..3db9a26f735c
--- /dev/null
+++ b/dom/bluetooth/BluetoothAdapter.h
@@ -0,0 +1,28 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=40: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_bluetooth_bluetoothadapter_h__
+#define mozilla_dom_bluetooth_bluetoothadapter_h__
+
+#include "BluetoothCommon.h"
+#include "nsIDOMBluetoothAdapter.h"
+
+BEGIN_BLUETOOTH_NAMESPACE
+
+class BluetoothAdapter : public nsIDOMBluetoothAdapter
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIDOMBLUETOOTHADAPTER
+
+  BluetoothAdapter();
+
+protected:
+  bool mPower;
+};
+
+END_BLUETOOTH_NAMESPACE
+#endif
diff --git a/dom/bluetooth/BluetoothCommon.h b/dom/bluetooth/BluetoothCommon.h
new file mode 100644
index 000000000000..e74435926527
--- /dev/null
+++ b/dom/bluetooth/BluetoothCommon.h
@@ -0,0 +1,19 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=40: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_bluetooth_bluetoothcommon_h__
+#define mozilla_dom_bluetooth_bluetoothcommon_h__
+
+#define BEGIN_BLUETOOTH_NAMESPACE \
+  namespace mozilla { namespace dom { namespace bluetooth {
+#define END_BLUETOOTH_NAMESPACE \
+  } /* namespace bluetooth */ } /* namespace dom */ } /* namespace mozilla */
+#define USING_BLUETOOTH_NAMESPACE \
+  using namespace mozilla::dom::bluetooth;
+
+class nsIDOMBluetooth;
+
+#endif // mozilla_dom_bluetooth_bluetoothcommon_h__
diff --git a/dom/bluetooth/Makefile.in b/dom/bluetooth/Makefile.in
new file mode 100644
index 000000000000..7d782e5356c9
--- /dev/null
+++ b/dom/bluetooth/Makefile.in
@@ -0,0 +1,30 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DEPTH            = ../..
+topsrcdir        = @top_srcdir@
+srcdir           = @srcdir@
+VPATH            = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+MODULE           = dom
+LIBRARY_NAME     = dombluetooth_s
+XPIDL_MODULE     = dom_bluetooth
+LIBXUL_LIBRARY   = 1
+FORCE_STATIC_LIB = 1
+
+include $(topsrcdir)/dom/dom-config.mk
+
+CPPSRCS = \
+  BluetoothAdapter.cpp \
+  $(NULL)
+
+XPIDLSRCS = \
+  nsIDOMNavigatorBluetooth.idl \
+  nsIDOMBluetoothAdapter.idl \
+  $(NULL)
+
+include $(topsrcdir)/config/rules.mk
+
diff --git a/dom/bluetooth/nsIDOMBluetoothAdapter.idl b/dom/bluetooth/nsIDOMBluetoothAdapter.idl
new file mode 100644
index 000000000000..1dc2e1265fd9
--- /dev/null
+++ b/dom/bluetooth/nsIDOMBluetoothAdapter.idl
@@ -0,0 +1,13 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=40: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+[scriptable, builtinclass, uuid(29689a22-45ff-4ccf-b552-5364ce3a3642)]
+interface nsIDOMBluetoothAdapter : nsISupports
+{
+  attribute boolean power;
+};
diff --git a/dom/bluetooth/nsIDOMNavigatorBluetooth.idl b/dom/bluetooth/nsIDOMNavigatorBluetooth.idl
new file mode 100644
index 000000000000..18f28de44f15
--- /dev/null
+++ b/dom/bluetooth/nsIDOMNavigatorBluetooth.idl
@@ -0,0 +1,15 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=40: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+interface nsIDOMBluetoothAdapter;
+
+[scriptable, uuid(677f2c2d-c4d1-41ea-addc-21d30d0d3858)]
+interface nsIDOMNavigatorBluetooth : nsISupports
+{
+  readonly attribute nsIDOMBluetoothAdapter mozBluetooth;
+};
diff --git a/dom/dom-config.mk b/dom/dom-config.mk
index ac61255020c0..69004508d428 100644
--- a/dom/dom-config.mk
+++ b/dom/dom-config.mk
@@ -31,5 +31,9 @@ DOM_SRCDIRS += \
   $(NULL)
 endif
 
+ifdef MOZ_B2G_BT
+DOM_SRCDIRS += dom/bluetooth
+endif
+
 LOCAL_INCLUDES += $(DOM_SRCDIRS:%=-I$(topsrcdir)/%)
 DEFINES += -D_IMPL_NS_LAYOUT
diff --git a/dom/plugins/base/android/ANPNativeWindow.cpp b/dom/plugins/base/android/ANPNativeWindow.cpp
index 4359688a859d..b693284f3d5b 100644
--- a/dom/plugins/base/android/ANPNativeWindow.cpp
+++ b/dom/plugins/base/android/ANPNativeWindow.cpp
@@ -52,14 +52,16 @@ using namespace mozilla;
 #define LOG(args...)  __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args)
 #define ASSIGN(obj, name)   (obj)->name = anp_native_window_##name
 
-static ANPNativeWindow anp_native_window_acquireNativeWindow(NPP instance) {
+static nsresult GetOwner(NPP instance, nsPluginInstanceOwner** owner) {
   nsNPAPIPluginInstance* pinst = static_cast<nsNPAPIPluginInstance*>(instance->ndata);
 
-  nsPluginInstanceOwner* owner;
-  if (NS_FAILED(pinst->GetOwner((nsIPluginInstanceOwner**)&owner))) {
-    return NULL;
-  }
+  return pinst->GetOwner((nsIPluginInstanceOwner**)owner);
+}
 
+static ANPNativeWindow anp_native_window_acquireNativeWindow(NPP instance) {
+  nsRefPtr<nsPluginInstanceOwner> owner;
+  if (NS_FAILED(GetOwner(instance, getter_AddRefs(owner))))
+    return NULL;
 
   ANPNativeWindow window = owner->Layer()->GetNativeWindowForContent();
   owner->Invalidate();
@@ -68,12 +70,9 @@ static ANPNativeWindow anp_native_window_acquireNativeWindow(NPP instance) {
 }
 
 static void anp_native_window_invertPluginContent(NPP instance, bool isContentInverted) {
-  nsNPAPIPluginInstance* pinst = static_cast<nsNPAPIPluginInstance*>(instance->ndata);
-
-  nsPluginInstanceOwner* owner;
-  if (NS_FAILED(pinst->GetOwner((nsIPluginInstanceOwner**)&owner))) {
+  nsRefPtr<nsPluginInstanceOwner> owner;
+  if (NS_FAILED(GetOwner(instance, getter_AddRefs(owner))))
     return;
-  }
 
   owner->Layer()->SetInverted(isContentInverted);
 }
diff --git a/dom/plugins/base/android/ANPVideo.cpp b/dom/plugins/base/android/ANPVideo.cpp
index 6b75425a046c..9f49c5f1909d 100644
--- a/dom/plugins/base/android/ANPVideo.cpp
+++ b/dom/plugins/base/android/ANPVideo.cpp
@@ -35,22 +35,23 @@
 
 using namespace mozilla;
 
-static AndroidMediaLayer* GetLayerForInstance(NPP instance) {
+static nsresult GetOwner(NPP instance, nsPluginInstanceOwner** owner) {
   nsNPAPIPluginInstance* pinst = static_cast<nsNPAPIPluginInstance*>(instance->ndata);
 
-  nsPluginInstanceOwner* owner;
-  if (NS_FAILED(pinst->GetOwner((nsIPluginInstanceOwner**)&owner))) {
-    return NULL;
-  }
+  return pinst->GetOwner((nsIPluginInstanceOwner**)owner);
+}
 
+static AndroidMediaLayer* GetLayerForInstance(NPP instance) {
+  nsRefPtr<nsPluginInstanceOwner> owner;
+  if (NS_FAILED(GetOwner(instance, getter_AddRefs(owner))))
+    return NULL;
+  
   return owner->Layer();
 }
 
 static void Invalidate(NPP instance) {
-  nsNPAPIPluginInstance* pinst = static_cast<nsNPAPIPluginInstance*>(instance->ndata);
-
-  nsPluginInstanceOwner* owner;
-  if (NS_FAILED(pinst->GetOwner((nsIPluginInstanceOwner**)&owner)))
+  nsRefPtr<nsPluginInstanceOwner> owner;
+  if (NS_FAILED(GetOwner(instance, getter_AddRefs(owner))))
     return;
 
   owner->Invalidate();
diff --git a/dom/plugins/base/android/ANPWindow.cpp b/dom/plugins/base/android/ANPWindow.cpp
index 75b643df386c..ca9c202a33d4 100644
--- a/dom/plugins/base/android/ANPWindow.cpp
+++ b/dom/plugins/base/android/ANPWindow.cpp
@@ -85,6 +85,12 @@ anp_window_requestCenterFitZoom(NPP instance)
   NOT_IMPLEMENTED();
 }
 
+static nsresult GetOwner(NPP instance, nsPluginInstanceOwner** owner) {
+  nsNPAPIPluginInstance* pinst = static_cast<nsNPAPIPluginInstance*>(instance->ndata);
+
+  return pinst->GetOwner((nsIPluginInstanceOwner**)owner);
+}
+
 ANPRectI
 anp_window_visibleRect(NPP instance)
 {
@@ -92,8 +98,8 @@ anp_window_visibleRect(NPP instance)
 
   nsNPAPIPluginInstance* pinst = static_cast<nsNPAPIPluginInstance*>(instance->ndata);
 
-  nsPluginInstanceOwner* owner;
-  if (NS_FAILED(pinst->GetOwner((nsIPluginInstanceOwner**)&owner))) {
+  nsRefPtr<nsPluginInstanceOwner> owner;
+  if (NS_FAILED(GetOwner(instance, getter_AddRefs(owner)))) {
     return rect;
   }
 
diff --git a/dom/plugins/base/nsNPAPIPluginInstance.cpp b/dom/plugins/base/nsNPAPIPluginInstance.cpp
index 0f90ad6ded6f..e77d98cca147 100644
--- a/dom/plugins/base/nsNPAPIPluginInstance.cpp
+++ b/dom/plugins/base/nsNPAPIPluginInstance.cpp
@@ -90,6 +90,7 @@ nsNPAPIPluginInstance::nsNPAPIPluginInstance(nsNPAPIPlugin* plugin)
 #ifdef MOZ_WIDGET_ANDROID
     mSurface(nsnull),
     mANPDrawingModel(0),
+    mOnScreen(true),
 #endif
     mRunning(NOT_STARTED),
     mWindowless(false),
@@ -724,6 +725,44 @@ void nsNPAPIPluginInstance::SetEventModel(NPEventModel aModel)
 #endif
 
 #if defined(MOZ_WIDGET_ANDROID)
+
+static void SendLifecycleEvent(nsNPAPIPluginInstance* aInstance, PRUint32 aAction)
+{
+  ANPEvent event;
+  event.inSize = sizeof(ANPEvent);
+  event.eventType = kLifecycle_ANPEventType;
+  event.data.lifecycle.action = aAction;
+  aInstance->HandleEvent(&event, nsnull);
+}
+
+void nsNPAPIPluginInstance::NotifyForeground(bool aForeground)
+{
+  PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("nsNPAPIPluginInstance::SetForeground this=%p\n foreground=%d",this, aForeground));
+  if (RUNNING != mRunning)
+    return;
+
+  SendLifecycleEvent(this, aForeground ? kResume_ANPLifecycleAction : kPause_ANPLifecycleAction);
+}
+
+void nsNPAPIPluginInstance::NotifyOnScreen(bool aOnScreen)
+{
+  PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("nsNPAPIPluginInstance::SetOnScreen this=%p\n onScreen=%d",this, aOnScreen));
+  if (RUNNING != mRunning || mOnScreen == aOnScreen)
+    return;
+
+  mOnScreen = aOnScreen;
+  SendLifecycleEvent(this, aOnScreen ? kOnScreen_ANPLifecycleAction : kOffScreen_ANPLifecycleAction);
+}
+
+void nsNPAPIPluginInstance::MemoryPressure()
+{
+  PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("nsNPAPIPluginInstance::MemoryPressure this=%p\n",this));
+  if (RUNNING != mRunning)
+    return;
+
+  SendLifecycleEvent(this, kFreeMemory_ANPLifecycleAction);
+}
+
 void nsNPAPIPluginInstance::SetANPDrawingModel(PRUint32 aModel)
 {
   mANPDrawingModel = aModel;
diff --git a/dom/plugins/base/nsNPAPIPluginInstance.h b/dom/plugins/base/nsNPAPIPluginInstance.h
index ca13f71accfa..38094948a7a6 100644
--- a/dom/plugins/base/nsNPAPIPluginInstance.h
+++ b/dom/plugins/base/nsNPAPIPluginInstance.h
@@ -147,6 +147,14 @@ public:
 #endif
 
 #ifdef MOZ_WIDGET_ANDROID
+  void NotifyForeground(bool aForeground);
+  void NotifyOnScreen(bool aOnScreen);
+  void MemoryPressure();
+
+  bool IsOnScreen() {
+    return mOnScreen;
+  }
+
   PRUint32 GetANPDrawingModel() { return mANPDrawingModel; }
   void SetANPDrawingModel(PRUint32 aModel);
 
@@ -282,6 +290,7 @@ private:
   bool mUsePluginLayersPref;
 #ifdef MOZ_WIDGET_ANDROID
   void* mSurface;
+  bool mOnScreen;
 #endif
 };
 
diff --git a/dom/plugins/base/nsPluginHost.cpp b/dom/plugins/base/nsPluginHost.cpp
index 94d2bee76f10..fa9cecdb5c85 100644
--- a/dom/plugins/base/nsPluginHost.cpp
+++ b/dom/plugins/base/nsPluginHost.cpp
@@ -375,6 +375,10 @@ nsPluginHost::nsPluginHost()
   if (obsService) {
     obsService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
     obsService->AddObserver(this, NS_PRIVATE_BROWSING_SWITCH_TOPIC, false);
+#ifdef MOZ_WIDGET_ANDROID
+    obsService->AddObserver(this, "application-foreground", false);
+    obsService->AddObserver(this, "application-background", false);
+#endif
   }
 
 #ifdef PLUGIN_LOGGING
@@ -3381,6 +3385,24 @@ NS_IMETHODIMP nsPluginHost::Observe(nsISupports *aSubject,
       mInstances[i]->PrivateModeStateChanged();
     }
   }
+#ifdef MOZ_WIDGET_ANDROID
+  if (!nsCRT::strcmp("application-background", aTopic)) {
+    for(PRUint32 i = 0; i < mInstances.Length(); i++) {
+      mInstances[i]->NotifyForeground(false);
+    }
+  }
+  if (!nsCRT::strcmp("application-foreground", aTopic)) {
+    for(PRUint32 i = 0; i < mInstances.Length(); i++) {
+      if (mInstances[i]->IsOnScreen())
+        mInstances[i]->NotifyForeground(true);
+    }
+  }
+  if (!nsCRT::strcmp("memory-pressure", aTopic)) {
+    for(PRUint32 i = 0; i < mInstances.Length(); i++) {
+      mInstances[i]->MemoryPressure();
+    }
+  }
+#endif
   return NS_OK;
 }
 
diff --git a/dom/plugins/base/nsPluginInstanceOwner.cpp b/dom/plugins/base/nsPluginInstanceOwner.cpp
index 05e39f1c8d99..b67de18fbcf2 100644
--- a/dom/plugins/base/nsPluginInstanceOwner.cpp
+++ b/dom/plugins/base/nsPluginInstanceOwner.cpp
@@ -339,9 +339,8 @@ nsPluginInstanceOwner::nsPluginInstanceOwner()
   mWaitingForPaint = false;
 
 #ifdef MOZ_WIDGET_ANDROID
-  mOnScreen = false;
   mInverted = false;
-  mLayer = new AndroidMediaLayer();
+  mLayer = nsnull;
 #endif
 }
 
@@ -393,10 +392,7 @@ nsPluginInstanceOwner::~nsPluginInstanceOwner()
   mPluginWindow = nsnull;
 
 #ifdef MOZ_WIDGET_ANDROID
-  if (mLayer) {
-    delete mLayer;
-    mLayer = nsnull;
-  }
+  RemovePluginView();
 #endif
 
   if (mInstance) {
@@ -1688,22 +1684,6 @@ void nsPluginInstanceOwner::SendSize(int width, int height)
   mInstance->HandleEvent(&event, nsnull);
 }
 
-void nsPluginInstanceOwner::SendOnScreenEvent(bool onScreen)
-{
-  if (!mInstance)
-    return;
-
-  if ((onScreen && !mOnScreen) || (!onScreen && mOnScreen)) {
-    ANPEvent event;
-    event.inSize = sizeof(ANPEvent);
-    event.eventType = kLifecycle_ANPEventType;
-    event.data.lifecycle.action = onScreen ? kOnScreen_ANPLifecycleAction : kOffScreen_ANPLifecycleAction;
-    mInstance->HandleEvent(&event, nsnull);
-
-    mOnScreen = onScreen;
-  }
-}
-
 bool nsPluginInstanceOwner::AddPluginView(const gfxRect& aRect)
 {
   void* javaSurface = mInstance->GetJavaSurface();
@@ -1754,14 +1734,12 @@ bool nsPluginInstanceOwner::AddPluginView(const gfxRect& aRect)
                             aRect.height);
 #endif
 
-  SendOnScreenEvent(true);
-
   return true;
 }
 
 void nsPluginInstanceOwner::RemovePluginView()
 {
-  if (!mInstance || !mObjectFrame | !mOnScreen)
+  if (!mInstance || !mObjectFrame)
     return;
 
   void* surface = mInstance->GetJavaSurface();
@@ -1779,7 +1757,6 @@ void nsPluginInstanceOwner::RemovePluginView()
                                             "removePluginView",
                                             "(Landroid/view/View;)V");
   env->CallStaticVoidMethod(cls, method, surface);
-  SendOnScreenEvent(false);
 }
 
 void nsPluginInstanceOwner::Invalidate() {
@@ -2759,6 +2736,14 @@ nsPluginInstanceOwner::Destroy()
   mContent->RemoveEventListener(NS_LITERAL_STRING("text"), this, true);
 #endif
 
+#if MOZ_WIDGET_ANDROID
+  RemovePluginView();
+
+  if (mLayer)
+    mLayer->SetVisible(false);
+
+#endif
+
   if (mWidget) {
     if (mPluginWindow) {
       mPluginWindow->SetPluginWidget(nsnull);
@@ -2867,7 +2852,7 @@ void nsPluginInstanceOwner::Paint(gfxContext* aContext,
                                   const gfxRect& aFrameRect,
                                   const gfxRect& aDirtyRect)
 {
-  if (!mInstance || !mObjectFrame)
+  if (!mInstance || !mObjectFrame || !mPluginDocumentActiveState)
     return;
 
   PRInt32 model = mInstance->GetANPDrawingModel();
@@ -2880,11 +2865,13 @@ void nsPluginInstanceOwner::Paint(gfxContext* aContext,
   }
 
   if (model == kOpenGL_ANPDrawingModel) {
+    if (!mLayer)
+      mLayer = new AndroidMediaLayer();
+
     // FIXME: this is gross
     float zoomLevel = aFrameRect.width / (float)mPluginWindow->width;
     mLayer->UpdatePosition(aFrameRect, zoomLevel);
 
-    SendOnScreenEvent(true);
     SendSize((int)aFrameRect.width, (int)aFrameRect.height);
     return;
   }
@@ -3587,17 +3574,6 @@ void nsPluginInstanceOwner::UpdateWindowPositionAndClipRect(bool aSetWindow)
   } else {
     mPluginWindow->clipRect.right = 0;
     mPluginWindow->clipRect.bottom = 0;
-#if 0 //MOZ_WIDGET_ANDROID
-    if (mInstance) {
-      PRInt32 model = mInstance->GetANPDrawingModel();
-
-      if (model == kSurface_ANPDrawingModel) {
-        RemovePluginView();
-      } else if (model == kOpenGL_ANPDrawingModel) {
-        HidePluginLayer();
-      }
-    }
-#endif
   }
 
   if (!aSetWindow)
@@ -3625,6 +3601,26 @@ nsPluginInstanceOwner::UpdateDocumentActiveState(bool aIsActive)
 {
   mPluginDocumentActiveState = aIsActive;
   UpdateWindowPositionAndClipRect(true);
+
+#ifdef MOZ_WIDGET_ANDROID
+  if (mInstance) {
+    if (mLayer)
+      mLayer->SetVisible(mPluginDocumentActiveState);
+
+    if (!mPluginDocumentActiveState)
+      RemovePluginView();
+
+    mInstance->NotifyOnScreen(mPluginDocumentActiveState);
+
+    // This is, perhaps, incorrect. It is supposed to be sent
+    // when "the webview has paused or resumed". The side effect
+    // is that Flash video players pause or resume (if they were
+    // playing before) based on the value here. I personally think
+    // we want that on Android when switching to another tab, so
+    // that's why we call it here.
+    mInstance->NotifyForeground(mPluginDocumentActiveState);
+  }
+#endif
 }
 #endif // XP_MACOSX
 
diff --git a/dom/plugins/base/nsPluginInstanceOwner.h b/dom/plugins/base/nsPluginInstanceOwner.h
index b328650548b6..4e4a029c2484 100644
--- a/dom/plugins/base/nsPluginInstanceOwner.h
+++ b/dom/plugins/base/nsPluginInstanceOwner.h
@@ -328,16 +328,14 @@ private:
   void FixUpURLS(const nsString &name, nsAString &value);
 #ifdef MOZ_WIDGET_ANDROID
   void SendSize(int width, int height);
-  void SendOnScreenEvent(bool onScreen);
 
   bool AddPluginView(const gfxRect& aRect);
   void RemovePluginView();
 
-  bool mOnScreen;
   bool mInverted;
 
   // For kOpenGL_ANPDrawingModel
-  mozilla::AndroidMediaLayer *mLayer;
+  nsRefPtr<mozilla::AndroidMediaLayer> mLayer;
 #endif 
  
   nsPluginNativeWindow       *mPluginWindow;
diff --git a/gfx/layers/basic/BasicImages.cpp b/gfx/layers/basic/BasicImages.cpp
index 5fd5942c9c08..90806cd38e1a 100644
--- a/gfx/layers/basic/BasicImages.cpp
+++ b/gfx/layers/basic/BasicImages.cpp
@@ -62,8 +62,9 @@ public:
   BasicPlanarYCbCrImage(const gfxIntSize& aScaleHint, gfxImageFormat aOffscreenFormat, BufferRecycleBin *aRecycleBin)
     : PlanarYCbCrImage(aRecycleBin)
     , mScaleHint(aScaleHint)
-    , mOffscreenFormat(aOffscreenFormat)
-  {}
+  {
+    SetOffscreenFormat(aOffscreenFormat);
+  }
 
   ~BasicPlanarYCbCrImage()
   {
@@ -79,7 +80,6 @@ public:
 
 private:
   gfxIntSize mScaleHint;
-  gfxImageFormat mOffscreenFormat;
   int mStride;
   nsAutoArrayPtr<PRUint8> mDecodedBuffer;
 };
diff --git a/hal/gonk/GonkHal.cpp b/hal/gonk/GonkHal.cpp
index c2abe7a22350..4c98315b003d 100644
--- a/hal/gonk/GonkHal.cpp
+++ b/hal/gonk/GonkHal.cpp
@@ -256,6 +256,10 @@ DisableBatteryNotifications()
 void
 GetCurrentBatteryInformation(hal::BatteryInformation *aBatteryInfo)
 {
+  static const int BATTERY_NOT_CHARGING = 0;
+  static const int BATTERY_CHARGING_USB = 1;
+  static const int BATTERY_CHARGING_AC  = 2;
+
   FILE *capacityFile = fopen("/sys/class/power_supply/battery/capacity", "r");
   double capacity = dom::battery::kDefaultLevel * 100;
   if (capacityFile) {
@@ -270,8 +274,17 @@ GetCurrentBatteryInformation(hal::BatteryInformation *aBatteryInfo)
     fclose(chargingFile);
   }
 
+  #ifdef DEBUG
+  if (chargingSrc != BATTERY_NOT_CHARGING &&
+      chargingSrc != BATTERY_CHARGING_USB &&
+      chargingSrc != BATTERY_CHARGING_AC) {
+    HAL_LOG(("charging_source contained unknown value: %d", chargingSrc));
+  }
+  #endif
+
   aBatteryInfo->level() = capacity / 100;
-  aBatteryInfo->charging() = chargingSrc == 1;
+  aBatteryInfo->charging() = (chargingSrc == BATTERY_CHARGING_USB ||
+                              chargingSrc == BATTERY_CHARGING_AC);
   aBatteryInfo->remainingTime() = dom::battery::kUnknownRemainingTime;
 }
 
diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp
index 9de5ebb31d37..21cf6810648a 100644
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -2660,8 +2660,9 @@ typedef struct JSDumpingTracer {
 } JSDumpingTracer;
 
 static void
-DumpNotify(JSTracer *trc, void *thing, JSGCTraceKind kind)
+DumpNotify(JSTracer *trc, void **thingp, JSGCTraceKind kind)
 {
+    void *thing = *thingp;
     JSDumpingTracer *dtrc;
     JSContext *cx;
     JSDHashEntryStub *entry;
diff --git a/js/src/jsapi.h b/js/src/jsapi.h
index 517cc4b36b38..e9a49a4eeba7 100644
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -3099,7 +3099,7 @@ JSVAL_TRACE_KIND(jsval v)
  * wants to use the existing liveness of entries.
  */
 typedef void
-(* JSTraceCallback)(JSTracer *trc, void *thing, JSGCTraceKind kind);
+(* JSTraceCallback)(JSTracer *trc, void **thingp, JSGCTraceKind kind);
 
 struct JSTracer {
     JSRuntime           *runtime;
diff --git a/js/src/jsfriendapi.cpp b/js/src/jsfriendapi.cpp
index e323ca29cac7..93d98bc54ed5 100644
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -483,13 +483,14 @@ struct JSDumpHeapTracer : public JSTracer {
 };
 
 static void
-DumpHeapVisitChild(JSTracer *trc, void *thing, JSGCTraceKind kind);
+DumpHeapVisitChild(JSTracer *trc, void **thingp, JSGCTraceKind kind);
 
 static void
-DumpHeapPushIfNew(JSTracer *trc, void *thing, JSGCTraceKind kind)
+DumpHeapPushIfNew(JSTracer *trc, void **thingp, JSGCTraceKind kind)
 {
     JS_ASSERT(trc->callback == DumpHeapPushIfNew ||
               trc->callback == DumpHeapVisitChild);
+    void *thing = *thingp;
     JSDumpHeapTracer *dtrc = static_cast<JSDumpHeapTracer *>(trc);
 
     /*
@@ -509,13 +510,13 @@ DumpHeapPushIfNew(JSTracer *trc, void *thing, JSGCTraceKind kind)
 }
 
 static void
-DumpHeapVisitChild(JSTracer *trc, void *thing, JSGCTraceKind kind)
+DumpHeapVisitChild(JSTracer *trc, void **thingp, JSGCTraceKind kind)
 {
     JS_ASSERT(trc->callback == DumpHeapVisitChild);
     JSDumpHeapTracer *dtrc = static_cast<JSDumpHeapTracer *>(trc);
     const char *edgeName = JS_GetTraceEdgeName(dtrc, dtrc->buffer, sizeof(dtrc->buffer));
-    fprintf(dtrc->output, "> %p %s\n", (void *)thing, edgeName);
-    DumpHeapPushIfNew(dtrc, thing, kind);
+    fprintf(dtrc->output, "> %p %s\n", *thingp, edgeName);
+    DumpHeapPushIfNew(dtrc, thingp, kind);
 }
 
 void
diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp
index 4304d25e858d..c63b0dcdefbd 100644
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -1859,7 +1859,7 @@ GCMarker::markDelayedChildren()
 
 #ifdef DEBUG
 static void
-EmptyMarkCallback(JSTracer *trc, void *thing, JSGCTraceKind kind)
+EmptyMarkCallback(JSTracer *trc, void **thingp, JSGCTraceKind kind)
 {
 }
 #endif
@@ -3456,7 +3456,7 @@ struct VerifyTracer : JSTracer {
  * node.
  */
 static void
-AccumulateEdge(JSTracer *jstrc, void *thing, JSGCTraceKind kind)
+AccumulateEdge(JSTracer *jstrc, void **thingp, JSGCTraceKind kind)
 {
     VerifyTracer *trc = (VerifyTracer *)jstrc;
 
@@ -3469,7 +3469,7 @@ AccumulateEdge(JSTracer *jstrc, void *thing, JSGCTraceKind kind)
     VerifyNode *node = trc->curnode;
     uint32_t i = node->count;
 
-    node->edges[i].thing = thing;
+    node->edges[i].thing = *thingp;
     node->edges[i].kind = kind;
     node->edges[i].label = trc->debugPrinter ? NULL : (char *)trc->debugPrintArg;
     node->count++;
@@ -3590,9 +3590,9 @@ oom:
 }
 
 static void
-CheckAutorooter(JSTracer *jstrc, void *thing, JSGCTraceKind kind)
+CheckAutorooter(JSTracer *jstrc, void **thingp, JSGCTraceKind kind)
 {
-    static_cast<Cell *>(thing)->markIfUnmarked();
+    static_cast<Cell *>(*thingp)->markIfUnmarked();
 }
 
 /*
@@ -3603,13 +3603,13 @@ CheckAutorooter(JSTracer *jstrc, void *thing, JSGCTraceKind kind)
  * modified) must point to marked objects.
  */
 static void
-CheckEdge(JSTracer *jstrc, void *thing, JSGCTraceKind kind)
+CheckEdge(JSTracer *jstrc, void **thingp, JSGCTraceKind kind)
 {
     VerifyTracer *trc = (VerifyTracer *)jstrc;
     VerifyNode *node = trc->curnode;
 
     for (uint32_t i = 0; i < node->count; i++) {
-        if (node->edges[i].thing == thing) {
+        if (node->edges[i].thing == *thingp) {
             JS_ASSERT(node->edges[i].kind == kind);
             node->edges[i].thing = NULL;
             return;
diff --git a/js/src/jsgcmark.cpp b/js/src/jsgcmark.cpp
index 6fbb4ff95cbe..feb1907918e4 100644
--- a/js/src/jsgcmark.cpp
+++ b/js/src/jsgcmark.cpp
@@ -107,10 +107,13 @@ MarkInternal(JSTracer *trc, T *thing)
      * GC.
      */
     if (!rt->gcCurrentCompartment || thing->compartment() == rt->gcCurrentCompartment) {
-        if (IS_GC_MARKING_TRACER(trc))
+        if (IS_GC_MARKING_TRACER(trc)) {
             PushMarkStack(static_cast<GCMarker *>(trc), thing);
-        else
-            trc->callback(trc, (void *)thing, GetGCThingTraceKind(thing));
+        } else {
+            void *tmp = (void *)thing;
+            trc->callback(trc, &tmp, GetGCThingTraceKind(thing));
+            JS_ASSERT(tmp == thing);
+        }
     }
 
 #ifdef DEBUG
diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp
index 3120bffb364a..7d1961996dc3 100644
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -3138,7 +3138,18 @@ BEGIN_CASE(JSOP_DEFCONST)
 BEGIN_CASE(JSOP_DEFVAR)
 {
     PropertyName *dn = atoms[GET_INDEX(regs.pc)]->asPropertyName();
-    if (!DefVarOrConstOperation(cx, op, dn, regs.fp()))
+
+    /* ES5 10.5 step 8 (with subsequent errata). */
+    uintN attrs = JSPROP_ENUMERATE;
+    if (!regs.fp()->isEvalFrame())
+        attrs |= JSPROP_PERMANENT;
+    if (op == JSOP_DEFCONST)
+        attrs |= JSPROP_READONLY;
+
+    /* Step 8b. */
+    JSObject &obj = regs.fp()->varObj();
+
+    if (!DefVarOrConstOperation(cx, obj, dn, attrs))
         goto error;
 }
 END_CASE(JSOP_DEFVAR)
diff --git a/js/src/jsinterpinlines.h b/js/src/jsinterpinlines.h
index 6426ff30bd38..c6167a356077 100644
--- a/js/src/jsinterpinlines.h
+++ b/js/src/jsinterpinlines.h
@@ -439,27 +439,19 @@ NameOperation(JSContext *cx, jsbytecode *pc, Value *vp)
 }
 
 inline bool
-DefVarOrConstOperation(JSContext *cx, JSOp op, PropertyName *dn, StackFrame *fp)
+DefVarOrConstOperation(JSContext *cx, JSObject &varobj, PropertyName *dn, uintN attrs)
 {
-    /* ES5 10.5 step 8 (with subsequent errata). */
-    uintN attrs = JSPROP_ENUMERATE;
-    if (!fp->isEvalFrame())
-        attrs |= JSPROP_PERMANENT;
-    if (op == JSOP_DEFCONST)
-        attrs |= JSPROP_READONLY;
-
-    /* Step 8b. */
-    JSObject &obj = fp->varObj();
-    JS_ASSERT(!obj.getOps()->defineProperty);
+    JS_ASSERT(varobj.isVarObj());
+    JS_ASSERT(!varobj.getOps()->defineProperty);
 
     JSProperty *prop;
     JSObject *obj2;
-    if (!obj.lookupProperty(cx, dn, &obj2, &prop))
+    if (!varobj.lookupProperty(cx, dn, &obj2, &prop))
         return false;
 
     /* Steps 8c, 8d. */
-    if (!prop || (obj2 != &obj && obj.isGlobal())) {
-        if (!DefineNativeProperty(cx, &obj, dn, UndefinedValue(),
+    if (!prop || (obj2 != &varobj && varobj.isGlobal())) {
+        if (!DefineNativeProperty(cx, &varobj, dn, UndefinedValue(),
                                   JS_PropertyStub, JS_StrictPropertyStub, attrs, 0, 0))
         {
             return false;
@@ -470,7 +462,7 @@ DefVarOrConstOperation(JSContext *cx, JSOp op, PropertyName *dn, StackFrame *fp)
          * see a redeclaration that's |const|, we consider it a conflict.
          */
         uintN oldAttrs;
-        if (!obj.getPropertyAttributes(cx, dn, &oldAttrs))
+        if (!varobj.getPropertyAttributes(cx, dn, &oldAttrs))
             return false;
         if (attrs & JSPROP_READONLY) {
             JSAutoByteString bytes;
diff --git a/js/src/jsopcode.cpp b/js/src/jsopcode.cpp
index ea7e6583517f..8e802bb73d51 100644
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -384,25 +384,25 @@ js_DisassembleAtPC(JSContext *cx, JSScript *script, JSBool lines, jsbytecode *pc
     jsbytecode *next, *end;
     uintN len;
 
-    SprintCString(sp, "loc   ");
+    sp->put("loc   ");
     if (lines)
-        SprintCString(sp, "line");
-    SprintCString(sp, "  op\n");
-    SprintCString(sp, "----- ");
+        sp->put("line");
+    sp->put("  op\n");
+    sp->put("----- ");
     if (lines)
-        SprintCString(sp, "----");
-    SprintCString(sp, "  --\n");
+        sp->put("----");
+    sp->put("  --\n");
 
     next = script->code;
     end = next + script->length;
     while (next < end) {
         if (next == script->main())
-            SprintCString(sp, "main:\n");
+            sp->put("main:\n");
         if (pc != NULL) {
             if (pc == next)
-                SprintCString(sp, "--> ");
+                sp->put("--> ");
             else
-                SprintCString(sp, "    ");
+                sp->put("    ");
         }
         len = js_Disassemble1(cx, script, next, next - script->code, lines, sp);
         if (!len)
@@ -716,7 +716,7 @@ js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc,
         return 0;
       }
     }
-    SprintCString(sp, "\n");
+    sp->put("\n");
     return len;
 }
 
@@ -863,6 +863,12 @@ Sprinter::put(const char *s, size_t len)
     return oldOffset;
 }
 
+ptrdiff_t
+Sprinter::put(const char *s)
+{
+    return put(s, strlen(s));
+}
+
 ptrdiff_t
 Sprinter::putString(JSString *s)
 {
@@ -934,24 +940,6 @@ Sprinter::getOffsetOf(const char *string) const
     return string - base;
 }
 
-ptrdiff_t
-js::SprintPut(Sprinter *sp, const char *s, size_t len)
-{
-    return sp->put(s, len);
-}
-
-ptrdiff_t
-js::SprintCString(Sprinter *sp, const char *s)
-{
-    return SprintPut(sp, s, strlen(s));
-}
-
-ptrdiff_t
-js::SprintString(Sprinter *sp, JSString *str)
-{
-    return sp->putString(str);
-}
-
 ptrdiff_t
 js::Sprint(Sprinter *sp, const char *format, ...)
 {
@@ -966,7 +954,7 @@ js::Sprint(Sprinter *sp, const char *format, ...)
         JS_ReportOutOfMemory(sp->context);
         return -1;
     }
-    offset = SprintCString(sp, bp);
+    offset = sp->put(bp);
     sp->context->free_(bp);
     return offset;
 }
@@ -1242,7 +1230,7 @@ js_printf(JSPrinter *jp, const char *format, ...)
     }
 
     cc = strlen(bp);
-    if (SprintPut(&jp->sprinter, bp, (size_t)cc) < 0)
+    if (jp->sprinter.put(bp, (size_t)cc) < 0)
         cc = -1;
     jp->sprinter.context->free_(bp);
 
@@ -1253,7 +1241,7 @@ js_printf(JSPrinter *jp, const char *format, ...)
 JSBool
 js_puts(JSPrinter *jp, const char *s)
 {
-    return SprintCString(&jp->sprinter, s) >= 0;
+    return jp->sprinter.put(s) >= 0;
 }
 
 /************************************************************************/
@@ -1342,7 +1330,7 @@ SprintOpcode(SprintStack *ss, const char *str, jsbytecode *pc,
     }
     ptrdiff_t offset = ss->sprinter.getOffset();
     UpdateDecompiledParent(ss->printer, pc, parentpc, offset - startOffset);
-    SprintCString(&ss->sprinter, str);
+    ss->sprinter.put(str);
 }
 
 /*
@@ -1425,7 +1413,7 @@ GetOff(SprintStack *ss, uintN i)
         if (!bytes)
             return 0;
         if (bytes != FAILED_EXPRESSION_DECOMPILER) {
-            off = SprintCString(&ss->sprinter, bytes);
+            off = ss->sprinter.put(bytes);
             if (off < 0)
                 off = 0;
             ss->offsets[i] = off;
@@ -1491,7 +1479,7 @@ PushOff(SprintStack *ss, ptrdiff_t off, JSOp op, jsbytecode *pc = NULL)
 static bool
 PushStr(SprintStack *ss, const char *str, JSOp op)
 {
-    ptrdiff_t off = SprintCString(&ss->sprinter, str);
+    ptrdiff_t off = ss->sprinter.put(str);
     if (off < 0)
         return false;
     return PushOff(ss, off, op);
@@ -1615,16 +1603,15 @@ SprintDoubleValue(Sprinter *sp, jsval v, JSOp *opp)
     JS_ASSERT(JSVAL_IS_DOUBLE(v));
     d = JSVAL_TO_DOUBLE(v);
     if (JSDOUBLE_IS_NEGZERO(d)) {
-        todo = SprintCString(sp, "-0");
+        todo = sp->put("-0");
         *opp = JSOP_NEG;
     } else if (!JSDOUBLE_IS_FINITE(d)) {
-        /* Don't use Infinity and NaN, they're mutable. */
-        todo = SprintCString(sp,
-                             JSDOUBLE_IS_NaN(d)
-                             ? "0 / 0"
-                             : (d < 0)
-                             ? "1 / -0"
-                             : "1 / 0");
+        /* Don't use Infinity and NaN, as local variables may shadow them. */
+        todo = sp->put(JSDOUBLE_IS_NaN(d)
+                       ? "0 / 0"
+                       : (d < 0)
+                       ? "1 / -0"
+                       : "1 / 0");
         *opp = JSOP_DIV;
     } else {
         ToCStringBuf cbuf;
@@ -1950,7 +1937,7 @@ DecompileDestructuringLHS(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc, JS
     switch (op) {
       case JSOP_POP:
         *hole = JS_TRUE;
-        if (SprintPut(&ss->sprinter, ", ", 2) < 0)
+        if (ss->sprinter.put(", ", 2) < 0)
             return NULL;
         break;
 
@@ -1994,7 +1981,7 @@ DecompileDestructuringLHS(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc, JS
          * in-place so pop/concat this pushed string.
          */
         lval = PopStr(ss, JSOP_NOP);
-        if (SprintCString(&ss->sprinter, lval) < 0)
+        if (ss->sprinter.put(lval) < 0)
             return NULL;
 
         LOCAL_ASSERT(*pc == JSOP_POP);
@@ -2034,7 +2021,7 @@ DecompileDestructuringLHS(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc, JS
                 return NULL;
         } else {
             lval = GetLocal(ss, i);
-            if (!lval || SprintCString(&ss->sprinter, lval) < 0)
+            if (!lval || ss->sprinter.put(lval) < 0)
                 return NULL;
         }
         if (op != JSOP_SETLOCALPOP) {
@@ -2071,10 +2058,10 @@ DecompileDestructuringLHS(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc, JS
         ss->sprinter.setOffset(todo);
         if (*lval == '\0') {
             /* lval is from JSOP_BINDNAME, so just print xval. */
-            todo = SprintCString(&ss->sprinter, xval);
+            todo = ss->sprinter.put(xval);
         } else if (*xval == '\0') {
             /* xval is from JSOP_SETCALL or JSOP_BINDXMLNAME, print lval. */
-            todo = SprintCString(&ss->sprinter, lval);
+            todo = ss->sprinter.put(lval);
         } else {
             todo = Sprint(&ss->sprinter,
                           (JOF_OPMODE(ss->opcodes[ss->top+1]) == JOF_XMLNAME)
@@ -2117,7 +2104,7 @@ DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc,
      * chars so the destructuring decompilation accumulates contiguously in
      * ss->sprinter starting with "[".
      */
-    ptrdiff_t head = SprintPut(&ss->sprinter, "[", 1);
+    ptrdiff_t head = ss->sprinter.put("[", 1);
     if (head < 0 || !PushOff(ss, head, JSOP_NOP))
         return NULL;
     ss->sprinter.setOffset(ss->sprinter.getOffset() - PAREN_SLOP);
@@ -2178,7 +2165,7 @@ DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc,
 
                 /* Fill in any holes (holes at the end don't matter). */
                 while (++lasti < i) {
-                    if (SprintPut(&ss->sprinter, ", ", 2) < 0)
+                    if (ss->sprinter.put(", ", 2) < 0)
                         return NULL;
                 }
             }
@@ -2196,7 +2183,7 @@ DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc,
 #endif
             if (!QuoteString(&ss->sprinter, atom, IsIdentifier(atom) ? 0 : (jschar)'\''))
                 return NULL;
-            if (SprintPut(&ss->sprinter, ": ", 2) < 0)
+            if (ss->sprinter.put(": ", 2) < 0)
                 return NULL;
             break;
           }
@@ -2268,7 +2255,7 @@ DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc,
             break;
         }
 
-        if (!hole && SprintPut(&ss->sprinter, ", ", 2) < 0)
+        if (!hole && ss->sprinter.put(", ", 2) < 0)
             return NULL;
 
         pc += JSOP_DUP_LENGTH;
@@ -2276,7 +2263,7 @@ DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc,
 
 out:
     const char *lval = ss->sprinter.stringAt(head);
-    if (SprintPut(&ss->sprinter, (*lval == '[') ? "]" : "}", 1) < 0)
+    if (ss->sprinter.put((*lval == '[') ? "]" : "}", 1) < 0)
         return NULL;
     return pc;
 }
@@ -2312,12 +2299,12 @@ DecompileGroupAssignment(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc,
         LOAD_OP_DATA(pc);
         if (op != JSOP_GETLOCAL)
             break;
-        if (!hole && SprintPut(&ss->sprinter, ", ", 2) < 0)
+        if (!hole && ss->sprinter.put(", ", 2) < 0)
             return NULL;
     }
 
     LOCAL_ASSERT(op == JSOP_POPN);
-    if (SprintPut(&ss->sprinter, "] = [", 5) < 0)
+    if (ss->sprinter.put("] = [", 5) < 0)
         return NULL;
 
     end = ss->top - 1;
@@ -2331,7 +2318,7 @@ DecompileGroupAssignment(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc,
         }
     }
 
-    if (SprintPut(&ss->sprinter, "]", 1) < 0)
+    if (ss->sprinter.put("]", 1) < 0)
         return NULL;
     ss->sprinter.setOffset(ss->offsets[i]);
     ss->top = start;
@@ -2867,7 +2854,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
                     (void)PopStr(ss, op, &lastlvalpc);
 
                     /* Print only the right operand of the assignment-op. */
-                    todo = SprintCString(&ss->sprinter, rval);
+                    todo = ss->sprinter.put(rval);
                 } else if (!inXML) {
                     rval = PopStrPrecDupe(ss, cs->prec + !!(cs->format & JOF_LEFTASSOC), &rvalpc);
                     lval = PopStrPrec(ss, cs->prec + !(cs->format & JOF_LEFTASSOC), &lvalpc);
@@ -2886,7 +2873,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
 
               case 1:
                 rval = PopStrDupe(ss, op, &rvalpc);
-                todo = SprintCString(&ss->sprinter, token);
+                todo = ss->sprinter.put(token);
                 SprintOpcode(ss, rval, rvalpc, pc, todo);
                 break;
 
@@ -2894,10 +2881,10 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
                 sn = js_GetSrcNote(jp->script, pc);
                 if (sn && SN_TYPE(sn) == SRC_CONTINUE) {
                     /* Hoisted let decl (e.g. 'y' in 'let (x) { let y; }'). */
-                    todo = SprintCString(&ss->sprinter, SkipString);
+                    todo = ss->sprinter.put(SkipString);
                     break;
                 }
-                todo = SprintCString(&ss->sprinter, token);
+                todo = ss->sprinter.put(token);
                 break;
 
               default:
@@ -3084,7 +3071,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
                             return NULL;
                         }
                     }
-                    if (SprintPut(&ss->sprinter, "]", 1) < 0)
+                    if (ss->sprinter.put("]", 1) < 0)
                         return NULL;
 
                     /*
@@ -3117,7 +3104,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
                      * NB: todo at this point indexes space in ss->sprinter
                      * that is liable to be overwritten.  The code below knows
                      * exactly how long rval lives, or else copies it down via
-                     * SprintCString.
+                     * Sprinter::put.
                      */
                     rval = ss->sprinter.stringAt(todo);
                     rvalpc = NULL;
@@ -3144,7 +3131,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
                              */
                             if (GET_UINT16(pc) == 0)
                                 break;
-                            todo = SprintCString(&ss->sprinter, rval);
+                            todo = ss->sprinter.put(rval);
                             saveop = JSOP_NOP;
                         }
                     }
@@ -3214,7 +3201,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
                     rval = PopStrDupe(ss, op, &rvalpc);
                     todo = ss->sprinter.getOffset();
                     SprintOpcode(ss, lval, lvalpc, pushpc, todo);
-                    SprintCString(&ss->sprinter, ", ");
+                    ss->sprinter.put(", ");
                     SprintOpcode(ss, rval, rvalpc, pushpc, todo);
                     break;
 
@@ -3423,7 +3410,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
                 ss->top = top;
                 ss->sprinter.setOffset(GetOff(ss, top));
                 if (op == JSOP_LEAVEBLOCKEXPR)
-                    todo = SprintCString(&ss->sprinter, rval);
+                    todo = ss->sprinter.put(rval);
                 break;
               }
 
@@ -3484,10 +3471,10 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
                         return NULL;
 
                     if (groupAssign) {
-                        if (SprintCString(&ss->sprinter, rhs) < 0)
+                        if (ss->sprinter.put(rhs) < 0)
                             return NULL;
                     } else if (!strncmp(rhs, DestructuredString, DestructuredStringLength)) {
-                        if (SprintCString(&ss->sprinter, rhs + DestructuredStringLength) < 0)
+                        if (ss->sprinter.put(rhs + DestructuredStringLength) < 0)
                             return NULL;
                     } else {
                         JS_ASSERT(atoms[i] != cx->runtime->atomState.emptyAtom);
@@ -3745,7 +3732,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
                                     ? "%s (%s)"
                                     : "%s %s",
                                     js_yield_str, rval)
-                           : SprintCString(&ss->sprinter, js_yield_str);
+                           : ss->sprinter.put(js_yield_str);
                     break;
                 }
 
@@ -4108,9 +4095,9 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
                     rval = PopStrDupe(ss, op, &rvalpc);
                     todo = ss->sprinter.getOffset();
                     SprintOpcode(ss, xval, xvalpc, pushpc, todo);
-                    SprintCString(&ss->sprinter, " ? ");
+                    ss->sprinter.put(" ? ");
                     SprintOpcode(ss, lval, lvalpc, pushpc, todo);
-                    SprintCString(&ss->sprinter, " : ");
+                    ss->sprinter.put(" : ");
                     SprintOpcode(ss, rval, rvalpc, pushpc, todo);
                     break;
 
@@ -4175,7 +4162,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
                 rval = POP_STR();
                 LOCAL_ASSERT(strcmp(rval, forelem_cookie) == 0);
                 if (*xval == '\0') {
-                    todo = SprintCString(&ss->sprinter, lval);
+                    todo = ss->sprinter.put(lval);
                 } else {
                     todo = Sprint(&ss->sprinter,
                                   (JOF_OPMODE(lastop) == JOF_XMLNAME)
@@ -4192,7 +4179,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
 
               case JSOP_DUP2:
                 rval = GetStr(ss, ss->top-2);
-                todo = SprintCString(&ss->sprinter, rval);
+                todo = ss->sprinter.put(rval);
                 if (todo < 0 || !PushOff(ss, todo,
                                          (JSOp) ss->opcodes[ss->top-2])) {
                     return NULL;
@@ -4311,7 +4298,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
 
                 rval = GetStr(ss, ss->top-1);
                 saveop = (JSOp) ss->opcodes[ss->top-1];
-                todo = SprintCString(&ss->sprinter, rval);
+                todo = ss->sprinter.put(rval);
                 break;
 
               case JSOP_SWAP:
@@ -4417,14 +4404,14 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
                     Sprint(&ss->sprinter, "%s ", js_new_str);
                 }
                 SprintOpcode(ss, argv[0], lvalpc, pc, todo);
-                SprintCString(&ss->sprinter, lval);
+                ss->sprinter.put(lval);
 
                 for (i = 1; i <= argc; i++) {
                     SprintOpcode(ss, argv[i], argbytecodes[i], pc, todo);
                     if (i < argc)
-                        SprintCString(&ss->sprinter, ", ");
+                        ss->sprinter.put(", ");
                 }
-                SprintCString(&ss->sprinter, rval);
+                ss->sprinter.put(rval);
 
                 cx->free_(argv);
                 cx->free_(argbytecodes);
@@ -4666,9 +4653,9 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
                 SprintOpcode(ss, lval, lvalpc, pc, todo);
                 if (*xval != '\0') {
                     bool xml = (JOF_OPMODE(lastop) == JOF_XMLNAME);
-                    SprintCString(&ss->sprinter, xml ? "." : "[");
+                    ss->sprinter.put(xml ? "." : "[");
                     SprintOpcode(ss, xval, xvalpc, pc, todo);
-                    SprintCString(&ss->sprinter, xml ? "" : "]");
+                    ss->sprinter.put(xml ? "" : "]");
                 }
                 break;
 
@@ -4690,9 +4677,9 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
                                           &lastlvalpc, &lastrvalpc);
                 todo = ss->sprinter.getOffset();
                 SprintOpcode(ss, lval, lvalpc, pc, todo);
-                SprintCString(&ss->sprinter, xml ? "." : "[");
+                ss->sprinter.put(xml ? "." : "[");
                 SprintOpcode(ss, xval, xvalpc, pc, todo);
-                SprintCString(&ss->sprinter, xml ? "" : "]");
+                ss->sprinter.put(xml ? "" : "]");
                 Sprint(&ss->sprinter, " %s= ", token);
                 SprintOpcode(ss, rval, rvalpc, pc, todo);
                 break;
@@ -4899,7 +4886,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
                     las.releaseEarly();
                     if (!rval)
                         return NULL;
-                    todo = SprintCString(&ss->sprinter, rval);
+                    todo = ss->sprinter.put(rval);
                     cx->free_((void *)rval);
                     break;
                 }
@@ -4923,12 +4910,12 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
                         return NULL;
                 }
               sprint_string:
-                todo = SprintString(&ss->sprinter, str);
+                todo = ss->sprinter.putString(str);
                 break;
 
               case JSOP_CALLEE:
                 JS_ASSERT(jp->fun && jp->fun->atom);
-                todo = SprintString(&ss->sprinter, jp->fun->atom);
+                todo = ss->sprinter.putString(jp->fun->atom);
                 break;
 
               case JSOP_OBJECT:
@@ -5140,7 +5127,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
                 goto do_function;
 
               case JSOP_HOLE:
-                todo = SprintPut(&ss->sprinter, "", 0);
+                todo = ss->sprinter.put("", 0);
                 break;
 
               case JSOP_NEWINIT:
@@ -5151,10 +5138,10 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
                 todo = ss->sprinter.getOffset();
                 if (i == JSProto_Array) {
                     ++ss->inArrayInit;
-                    if (SprintCString(&ss->sprinter, "[") < 0)
+                    if (ss->sprinter.put("[") < 0)
                         return NULL;
                 } else {
-                    if (SprintCString(&ss->sprinter, "{") < 0)
+                    if (ss->sprinter.put("{") < 0)
                         return NULL;
                 }
                 break;
@@ -5164,7 +5151,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
               {
                 todo = ss->sprinter.getOffset();
                 ++ss->inArrayInit;
-                if (SprintCString(&ss->sprinter, "[") < 0)
+                if (ss->sprinter.put("[") < 0)
                     return NULL;
                 break;
               }
@@ -5172,7 +5159,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
               case JSOP_NEWOBJECT:
               {
                 todo = ss->sprinter.getOffset();
-                if (SprintCString(&ss->sprinter, "{") < 0)
+                if (ss->sprinter.put("{") < 0)
                     return NULL;
                 break;
               }
@@ -5283,9 +5270,9 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
               case JSOP_ANYNAME:
                 if (pc[JSOP_ANYNAME_LENGTH] == JSOP_TOATTRNAME) {
                     len += JSOP_TOATTRNAME_LENGTH;
-                    todo = SprintPut(&ss->sprinter, "@*", 2);
+                    todo = ss->sprinter.put("@*", 2);
                 } else {
-                    todo = SprintPut(&ss->sprinter, "*", 1);
+                    todo = ss->sprinter.put("*", 1);
                 }
                 break;
 
@@ -5393,18 +5380,18 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
 
               case JSOP_XMLCDATA:
                 LOAD_ATOM(0);
-                todo = SprintPut(&ss->sprinter, "<![CDATA[", 9);
+                todo = ss->sprinter.put("<![CDATA[", 9);
                 if (!QuoteString(&ss->sprinter, atom, DONT_ESCAPE))
                     return NULL;
-                SprintPut(&ss->sprinter, "]]>", 3);
+                ss->sprinter.put("]]>", 3);
                 break;
 
               case JSOP_XMLCOMMENT:
                 LOAD_ATOM(0);
-                todo = SprintPut(&ss->sprinter, "<!--", 4);
+                todo = ss->sprinter.put("<!--", 4);
                 if (!QuoteString(&ss->sprinter, atom, DONT_ESCAPE))
                     return NULL;
-                SprintPut(&ss->sprinter, "-->", 3);
+                ss->sprinter.put("-->", 3);
                 break;
 
               case JSOP_XMLPI:
@@ -5412,19 +5399,19 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
                 rval = JS_strdup(cx, POP_STR());
                 if (!rval)
                     return NULL;
-                todo = SprintPut(&ss->sprinter, "<?", 2);
+                todo = ss->sprinter.put("<?", 2);
                 ok = QuoteString(&ss->sprinter, atom, 0) &&
                      (*rval == '\0' ||
-                      (SprintPut(&ss->sprinter, " ", 1) >= 0 &&
-                       SprintCString(&ss->sprinter, rval)));
+                      (ss->sprinter.put(" ", 1) >= 0 &&
+                       ss->sprinter.put(rval)));
                 cx->free_((char *)rval);
                 if (!ok)
                     return NULL;
-                SprintPut(&ss->sprinter, "?>", 2);
+                ss->sprinter.put("?>", 2);
                 break;
 
               case JSOP_GETFUNNS:
-                todo = SprintPut(&ss->sprinter, js_function_str, 8);
+                todo = ss->sprinter.put(js_function_str, 8);
                 break;
 #endif /* JS_HAS_XML_SUPPORT */
 
@@ -5673,7 +5660,7 @@ js_DecompileFunction(JSPrinter *jp)
                 LOCAL_ASSERT(*pc == JSOP_POP);
                 pc += JSOP_POP_LENGTH;
                 lval = PopStr(&ss, JSOP_NOP);
-                todo = SprintCString(&jp->sprinter, lval);
+                todo = jp->sprinter.put(lval);
                 if (todo < 0) {
                     ok = JS_FALSE;
                     break;
diff --git a/js/src/jsopcode.h b/js/src/jsopcode.h
index fa6134b53124..e931ab029290 100644
--- a/js/src/jsopcode.h
+++ b/js/src/jsopcode.h
@@ -504,6 +504,7 @@ class Sprinter
      * the beginning of this new data
      */
     ptrdiff_t put(const char *s, size_t len);
+    ptrdiff_t put(const char *s);
     ptrdiff_t putString(JSString *str);
 
     /* Prints a formatted string into the buffer */
@@ -518,15 +519,6 @@ class Sprinter
     ptrdiff_t getOffsetOf(const char *string) const;
 };
 
-extern ptrdiff_t
-SprintPut(Sprinter *sp, const char *s, size_t len);
-
-extern ptrdiff_t
-SprintCString(Sprinter *sp, const char *s);
-
-extern ptrdiff_t
-SprintString(Sprinter *sp, JSString *str);
-
 extern ptrdiff_t
 Sprint(Sprinter *sp, const char *format, ...);
 
diff --git a/js/src/methodjit/StubCalls.cpp b/js/src/methodjit/StubCalls.cpp
index 8de50b4f1901..c9a8d113432c 100644
--- a/js/src/methodjit/StubCalls.cpp
+++ b/js/src/methodjit/StubCalls.cpp
@@ -1665,7 +1665,15 @@ stubs::DelElem(VMFrame &f)
 void JS_FASTCALL
 stubs::DefVarOrConst(VMFrame &f, PropertyName *dn)
 {
-    if (!DefVarOrConstOperation(f.cx, JSOp(*f.regs.pc), dn, f.fp()))
+    uintN attrs = JSPROP_ENUMERATE;
+    if (!f.fp()->isEvalFrame())
+        attrs |= JSPROP_PERMANENT;
+    if (JSOp(*f.regs.pc) == JSOP_DEFCONST)
+        attrs |= JSPROP_READONLY;
+
+    JSObject &obj = f.fp()->varObj();
+
+    if (!DefVarOrConstOperation(f.cx, obj, dn, attrs))
         THROW();
 }
 
diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp
index 442556f14a9f..dfebcb24af9c 100644
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -1447,11 +1447,12 @@ typedef struct JSCountHeapTracer {
 } JSCountHeapTracer;
 
 static void
-CountHeapNotify(JSTracer *trc, void *thing, JSGCTraceKind kind)
+CountHeapNotify(JSTracer *trc, void **thingp, JSGCTraceKind kind)
 {
     JSCountHeapTracer *countTracer;
     JSDHashEntryStub *entry;
     JSCountHeapNode *node;
+    void *thing = *thingp;
 
     JS_ASSERT(trc->callback == CountHeapNotify);
     countTracer = (JSCountHeapTracer *)trc;
diff --git a/js/src/shell/jsheaptools.cpp b/js/src/shell/jsheaptools.cpp
index eda952c94ea5..04ed84d0c729 100644
--- a/js/src/shell/jsheaptools.cpp
+++ b/js/src/shell/jsheaptools.cpp
@@ -232,7 +232,7 @@ class HeapReverser : public JSTracer {
      * A stack of work items. We represent the stack explicitly to avoid
      * overflowing the C++ stack when traversing long chains of objects.
      */
-    Vector<Child> work; 
+    Vector<Child> work;
 
     /* When traverseEdge is called, the Cell and kind at which the edge originated. */
     void *parent;
@@ -249,19 +249,17 @@ class HeapReverser : public JSTracer {
     bool traversalStatus;
 
     /* Static member function wrapping 'traverseEdge'. */
-    static void traverseEdgeWithThis(JSTracer *tracer, void *cell, JSGCTraceKind kind) {
+    static void traverseEdgeWithThis(JSTracer *tracer, void **thingp, JSGCTraceKind kind) {
         HeapReverser *reverser = static_cast<HeapReverser *>(tracer);
-        reverser->traversalStatus = reverser->traverseEdge(cell, kind);
+        reverser->traversalStatus = reverser->traverseEdge(*thingp, kind);
     }
 
     /* Return a jsval representing a node, if possible; otherwise, return JSVAL_VOID. */
     jsval nodeToValue(void *cell, int kind) {
-        if (kind == JSTRACE_OBJECT) {
-            JSObject *object = static_cast<JSObject *>(cell);
-            return OBJECT_TO_JSVAL(object);
-        } else {
+        if (kind != JSTRACE_OBJECT)
             return JSVAL_VOID;
-        }
+        JSObject *object = static_cast<JSObject *>(cell);
+        return OBJECT_TO_JSVAL(object);
     }
 };
 
diff --git a/js/src/tests/ecma_5/String/string-upper-lower-mapping.js b/js/src/tests/ecma_5/String/string-upper-lower-mapping.js
index 93a7deef4617..35ea051c0115 100644
--- a/js/src/tests/ecma_5/String/string-upper-lower-mapping.js
+++ b/js/src/tests/ecma_5/String/string-upper-lower-mapping.js
@@ -619,7 +619,7 @@ var mapping = [
   [0x194, 0x263], /* LATIN SMALL LETTER GAMMA */
   [0x264, 0x264], /* LATIN SMALL LETTER RAMS HORN (LATIN SMALL LETTER BABY GAMMA) */
   [0xa78d, 0x265], /* LATIN SMALL LETTER TURNED H */
-  [0x266, 0x266], /* LATIN SMALL LETTER H WITH HOOK (LATIN SMALL LETTER H HOOK) */
+  [0xa7aa, 0x266], /* LATIN SMALL LETTER H WITH HOOK (LATIN SMALL LETTER H HOOK) */
   [0x267, 0x267], /* LATIN SMALL LETTER HENG WITH HOOK (LATIN SMALL LETTER HENG HOOK) */
   [0x197, 0x268], /* LATIN SMALL LETTER I WITH STROKE (LATIN SMALL LETTER BARRED I) */
   [0x196, 0x269], /* LATIN SMALL LETTER IOTA */
@@ -1428,7 +1428,7 @@ var mapping = [
   [0x58c, 0x58c],
   [0x58d, 0x58d],
   [0x58e, 0x58e],
-  [0x58f, 0x58f],
+  [0x58f, 0x58f], /* ARMENIAN DRAM SIGN */
   [0x590, 0x590],
   [0x591, 0x591], /* HEBREW ACCENT ETNAHTA */
   [0x592, 0x592], /* HEBREW ACCENT SEGOL */
@@ -1545,7 +1545,7 @@ var mapping = [
   [0x601, 0x601], /* ARABIC SIGN SANAH */
   [0x602, 0x602], /* ARABIC FOOTNOTE MARKER */
   [0x603, 0x603], /* ARABIC SIGN SAFHA */
-  [0x604, 0x604],
+  [0x604, 0x604], /* ARABIC SIGN SAMVAT */
   [0x605, 0x605],
   [0x606, 0x606], /* ARABIC-INDIC CUBE ROOT */
   [0x607, 0x607], /* ARABIC-INDIC FOURTH ROOT */
@@ -2213,19 +2213,19 @@ var mapping = [
   [0x89d, 0x89d],
   [0x89e, 0x89e],
   [0x89f, 0x89f],
-  [0x8a0, 0x8a0],
+  [0x8a0, 0x8a0], /* ARABIC LETTER BEH WITH SMALL V BELOW */
   [0x8a1, 0x8a1],
-  [0x8a2, 0x8a2],
-  [0x8a3, 0x8a3],
-  [0x8a4, 0x8a4],
-  [0x8a5, 0x8a5],
-  [0x8a6, 0x8a6],
-  [0x8a7, 0x8a7],
-  [0x8a8, 0x8a8],
-  [0x8a9, 0x8a9],
-  [0x8aa, 0x8aa],
-  [0x8ab, 0x8ab],
-  [0x8ac, 0x8ac],
+  [0x8a2, 0x8a2], /* ARABIC LETTER JEEM WITH TWO DOTS ABOVE */
+  [0x8a3, 0x8a3], /* ARABIC LETTER TAH WITH TWO DOTS ABOVE */
+  [0x8a4, 0x8a4], /* ARABIC LETTER FEH WITH DOT BELOW AND THREE DOTS ABOVE */
+  [0x8a5, 0x8a5], /* ARABIC LETTER QAF WITH DOT BELOW */
+  [0x8a6, 0x8a6], /* ARABIC LETTER LAM WITH DOUBLE BAR */
+  [0x8a7, 0x8a7], /* ARABIC LETTER MEEM WITH THREE DOTS ABOVE */
+  [0x8a8, 0x8a8], /* ARABIC LETTER YEH WITH TWO DOTS BELOW AND HAMZA ABOVE */
+  [0x8a9, 0x8a9], /* ARABIC LETTER YEH WITH TWO DOTS BELOW AND DOT ABOVE */
+  [0x8aa, 0x8aa], /* ARABIC LETTER REH WITH LOOP */
+  [0x8ab, 0x8ab], /* ARABIC LETTER WAW WITH DOT WITHIN */
+  [0x8ac, 0x8ac], /* ARABIC LETTER ROHINGYA YEH */
   [0x8ad, 0x8ad],
   [0x8ae, 0x8ae],
   [0x8af, 0x8af],
@@ -2281,33 +2281,33 @@ var mapping = [
   [0x8e1, 0x8e1],
   [0x8e2, 0x8e2],
   [0x8e3, 0x8e3],
-  [0x8e4, 0x8e4],
-  [0x8e5, 0x8e5],
-  [0x8e6, 0x8e6],
-  [0x8e7, 0x8e7],
-  [0x8e8, 0x8e8],
-  [0x8e9, 0x8e9],
-  [0x8ea, 0x8ea],
-  [0x8eb, 0x8eb],
-  [0x8ec, 0x8ec],
-  [0x8ed, 0x8ed],
-  [0x8ee, 0x8ee],
-  [0x8ef, 0x8ef],
-  [0x8f0, 0x8f0],
-  [0x8f1, 0x8f1],
-  [0x8f2, 0x8f2],
-  [0x8f3, 0x8f3],
-  [0x8f4, 0x8f4],
-  [0x8f5, 0x8f5],
-  [0x8f6, 0x8f6],
-  [0x8f7, 0x8f7],
-  [0x8f8, 0x8f8],
-  [0x8f9, 0x8f9],
-  [0x8fa, 0x8fa],
-  [0x8fb, 0x8fb],
-  [0x8fc, 0x8fc],
-  [0x8fd, 0x8fd],
-  [0x8fe, 0x8fe],
+  [0x8e4, 0x8e4], /* ARABIC CURLY FATHA */
+  [0x8e5, 0x8e5], /* ARABIC CURLY DAMMA */
+  [0x8e6, 0x8e6], /* ARABIC CURLY KASRA */
+  [0x8e7, 0x8e7], /* ARABIC CURLY FATHATAN */
+  [0x8e8, 0x8e8], /* ARABIC CURLY DAMMATAN */
+  [0x8e9, 0x8e9], /* ARABIC CURLY KASRATAN */
+  [0x8ea, 0x8ea], /* ARABIC TONE ONE DOT ABOVE */
+  [0x8eb, 0x8eb], /* ARABIC TONE TWO DOTS ABOVE */
+  [0x8ec, 0x8ec], /* ARABIC TONE LOOP ABOVE */
+  [0x8ed, 0x8ed], /* ARABIC TONE ONE DOT BELOW */
+  [0x8ee, 0x8ee], /* ARABIC TONE TWO DOTS BELOW */
+  [0x8ef, 0x8ef], /* ARABIC TONE LOOP BELOW */
+  [0x8f0, 0x8f0], /* ARABIC OPEN FATHATAN */
+  [0x8f1, 0x8f1], /* ARABIC OPEN DAMMATAN */
+  [0x8f2, 0x8f2], /* ARABIC OPEN KASRATAN */
+  [0x8f3, 0x8f3], /* ARABIC SMALL HIGH WAW */
+  [0x8f4, 0x8f4], /* ARABIC FATHA WITH RING */
+  [0x8f5, 0x8f5], /* ARABIC FATHA WITH DOT ABOVE */
+  [0x8f6, 0x8f6], /* ARABIC KASRA WITH DOT BELOW */
+  [0x8f7, 0x8f7], /* ARABIC LEFT ARROWHEAD ABOVE */
+  [0x8f8, 0x8f8], /* ARABIC RIGHT ARROWHEAD ABOVE */
+  [0x8f9, 0x8f9], /* ARABIC LEFT ARROWHEAD BELOW */
+  [0x8fa, 0x8fa], /* ARABIC RIGHT ARROWHEAD BELOW */
+  [0x8fb, 0x8fb], /* ARABIC DOUBLE RIGHT ARROWHEAD ABOVE */
+  [0x8fc, 0x8fc], /* ARABIC DOUBLE RIGHT ARROWHEAD ABOVE WITH DOT */
+  [0x8fd, 0x8fd], /* ARABIC RIGHT ARROWHEAD ABOVE WITH DOT */
+  [0x8fe, 0x8fe], /* ARABIC DAMMA WITH DOT */
   [0x8ff, 0x8ff],
   [0x900, 0x900], /* DEVANAGARI SIGN INVERTED CANDRABINDU */
   [0x901, 0x901], /* DEVANAGARI SIGN CANDRABINDU */
@@ -2805,7 +2805,7 @@ var mapping = [
   [0xaed, 0xaed], /* GUJARATI DIGIT SEVEN */
   [0xaee, 0xaee], /* GUJARATI DIGIT EIGHT */
   [0xaef, 0xaef], /* GUJARATI DIGIT NINE */
-  [0xaf0, 0xaf0],
+  [0xaf0, 0xaf0], /* GUJARATI ABBREVIATION SIGN */
   [0xaf1, 0xaf1], /* GUJARATI RUPEE SIGN */
   [0xaf2, 0xaf2],
   [0xaf3, 0xaf3],
@@ -3811,8 +3811,8 @@ var mapping = [
   [0xedb, 0xedb],
   [0xedc, 0xedc], /* LAO HO NO */
   [0xedd, 0xedd], /* LAO HO MO */
-  [0xede, 0xede],
-  [0xedf, 0xedf],
+  [0xede, 0xede], /* LAO LETTER KHMU GO */
+  [0xedf, 0xedf], /* LAO LETTER KHMU NYO */
   [0xee0, 0xee0],
   [0xee1, 0xee1],
   [0xee2, 0xee2],
@@ -4300,13 +4300,13 @@ var mapping = [
   [0x10c4, 0x2d24], /* GEORGIAN CAPITAL LETTER HAR */
   [0x10c5, 0x2d25], /* GEORGIAN CAPITAL LETTER HOE */
   [0x10c6, 0x10c6],
-  [0x10c7, 0x10c7],
+  [0x10c7, 0x2d27], /* GEORGIAN CAPITAL LETTER YN */
   [0x10c8, 0x10c8],
   [0x10c9, 0x10c9],
   [0x10ca, 0x10ca],
   [0x10cb, 0x10cb],
   [0x10cc, 0x10cc],
-  [0x10cd, 0x10cd],
+  [0x10cd, 0x2d2d], /* GEORGIAN CAPITAL LETTER AEN */
   [0x10ce, 0x10ce],
   [0x10cf, 0x10cf],
   [0x10d0, 0x10d0], /* GEORGIAN LETTER AN (GEORGIAN SMALL LETTER AN) */
@@ -4354,9 +4354,9 @@ var mapping = [
   [0x10fa, 0x10fa], /* GEORGIAN LETTER AIN */
   [0x10fb, 0x10fb], /* GEORGIAN PARAGRAPH SEPARATOR */
   [0x10fc, 0x10fc], /* MODIFIER LETTER GEORGIAN NAR */
-  [0x10fd, 0x10fd],
-  [0x10fe, 0x10fe],
-  [0x10ff, 0x10ff],
+  [0x10fd, 0x10fd], /* GEORGIAN LETTER AEN */
+  [0x10fe, 0x10fe], /* GEORGIAN LETTER HARD SIGN */
+  [0x10ff, 0x10ff], /* GEORGIAN LETTER LABIAL SIGN */
   [0x1100, 0x1100], /* HANGUL CHOSEONG KIYEOK */
   [0x1101, 0x1101], /* HANGUL CHOSEONG SSANGKIYEOK */
   [0x1102, 0x1102], /* HANGUL CHOSEONG NIEUN */
@@ -7088,9 +7088,9 @@ var mapping = [
   [0x1ba8, 0x1ba8], /* SUNDANESE VOWEL SIGN PAMEPET */
   [0x1ba9, 0x1ba9], /* SUNDANESE VOWEL SIGN PANEULEUNG */
   [0x1baa, 0x1baa], /* SUNDANESE SIGN PAMAAEH */
-  [0x1bab, 0x1bab],
-  [0x1bac, 0x1bac],
-  [0x1bad, 0x1bad],
+  [0x1bab, 0x1bab], /* SUNDANESE SIGN VIRAMA */
+  [0x1bac, 0x1bac], /* SUNDANESE CONSONANT SIGN PASANGAN MA */
+  [0x1bad, 0x1bad], /* SUNDANESE CONSONANT SIGN PASANGAN WA */
   [0x1bae, 0x1bae], /* SUNDANESE LETTER KHA */
   [0x1baf, 0x1baf], /* SUNDANESE LETTER SYA */
   [0x1bb0, 0x1bb0], /* SUNDANESE DIGIT ZERO */
@@ -7103,12 +7103,12 @@ var mapping = [
   [0x1bb7, 0x1bb7], /* SUNDANESE DIGIT SEVEN */
   [0x1bb8, 0x1bb8], /* SUNDANESE DIGIT EIGHT */
   [0x1bb9, 0x1bb9], /* SUNDANESE DIGIT NINE */
-  [0x1bba, 0x1bba],
-  [0x1bbb, 0x1bbb],
-  [0x1bbc, 0x1bbc],
-  [0x1bbd, 0x1bbd],
-  [0x1bbe, 0x1bbe],
-  [0x1bbf, 0x1bbf],
+  [0x1bba, 0x1bba], /* SUNDANESE AVAGRAHA */
+  [0x1bbb, 0x1bbb], /* SUNDANESE LETTER REU */
+  [0x1bbc, 0x1bbc], /* SUNDANESE LETTER LEU */
+  [0x1bbd, 0x1bbd], /* SUNDANESE LETTER BHA */
+  [0x1bbe, 0x1bbe], /* SUNDANESE LETTER FINAL K */
+  [0x1bbf, 0x1bbf], /* SUNDANESE LETTER FINAL M */
   [0x1bc0, 0x1bc0], /* BATAK LETTER A */
   [0x1bc1, 0x1bc1], /* BATAK LETTER SIMALUNGUN A */
   [0x1bc2, 0x1bc2], /* BATAK LETTER HA */
@@ -7365,14 +7365,14 @@ var mapping = [
   [0x1cbd, 0x1cbd],
   [0x1cbe, 0x1cbe],
   [0x1cbf, 0x1cbf],
-  [0x1cc0, 0x1cc0],
-  [0x1cc1, 0x1cc1],
-  [0x1cc2, 0x1cc2],
-  [0x1cc3, 0x1cc3],
-  [0x1cc4, 0x1cc4],
-  [0x1cc5, 0x1cc5],
-  [0x1cc6, 0x1cc6],
-  [0x1cc7, 0x1cc7],
+  [0x1cc0, 0x1cc0], /* SUNDANESE PUNCTUATION BINDU SURYA */
+  [0x1cc1, 0x1cc1], /* SUNDANESE PUNCTUATION BINDU PANGLONG */
+  [0x1cc2, 0x1cc2], /* SUNDANESE PUNCTUATION BINDU PURNAMA */
+  [0x1cc3, 0x1cc3], /* SUNDANESE PUNCTUATION BINDU CAKRA */
+  [0x1cc4, 0x1cc4], /* SUNDANESE PUNCTUATION BINDU LEU SATANGA */
+  [0x1cc5, 0x1cc5], /* SUNDANESE PUNCTUATION BINDU KA SATANGA */
+  [0x1cc6, 0x1cc6], /* SUNDANESE PUNCTUATION BINDU DA SATANGA */
+  [0x1cc7, 0x1cc7], /* SUNDANESE PUNCTUATION BINDU BA SATANGA */
   [0x1cc8, 0x1cc8],
   [0x1cc9, 0x1cc9],
   [0x1cca, 0x1cca],
@@ -7416,10 +7416,10 @@ var mapping = [
   [0x1cf0, 0x1cf0], /* VEDIC SIGN RTHANG LONG ANUSVARA */
   [0x1cf1, 0x1cf1], /* VEDIC SIGN ANUSVARA UBHAYATO MUKHA */
   [0x1cf2, 0x1cf2], /* VEDIC SIGN ARDHAVISARGA */
-  [0x1cf3, 0x1cf3],
-  [0x1cf4, 0x1cf4],
-  [0x1cf5, 0x1cf5],
-  [0x1cf6, 0x1cf6],
+  [0x1cf3, 0x1cf3], /* VEDIC SIGN ROTATED ARDHAVISARGA */
+  [0x1cf4, 0x1cf4], /* VEDIC TONE CANDRA ABOVE */
+  [0x1cf5, 0x1cf5], /* VEDIC SIGN JIHVAMULIYA */
+  [0x1cf6, 0x1cf6], /* VEDIC SIGN UPADHMANIYA */
   [0x1cf7, 0x1cf7],
   [0x1cf8, 0x1cf8],
   [0x1cf9, 0x1cf9],
@@ -10192,9 +10192,9 @@ var mapping = [
   [0x27c8, 0x27c8], /* REVERSE SOLIDUS PRECEDING SUBSET */
   [0x27c9, 0x27c9], /* SUPERSET PRECEDING SOLIDUS */
   [0x27ca, 0x27ca], /* VERTICAL BAR WITH HORIZONTAL STROKE */
-  [0x27cb, 0x27cb],
+  [0x27cb, 0x27cb], /* MATHEMATICAL RISING DIAGONAL */
   [0x27cc, 0x27cc], /* LONG DIVISION */
-  [0x27cd, 0x27cd],
+  [0x27cd, 0x27cd], /* MATHEMATICAL FALLING DIAGONAL */
   [0x27ce, 0x27ce], /* SQUARED LOGICAL AND */
   [0x27cf, 0x27cf], /* SQUARED LOGICAL OR */
   [0x27d0, 0x27d0], /* WHITE DIAMOND WITH CENTRED DOT */
@@ -11511,8 +11511,8 @@ var mapping = [
   [0x2cef, 0x2cef], /* COPTIC COMBINING NI ABOVE */
   [0x2cf0, 0x2cf0], /* COPTIC COMBINING SPIRITUS ASPER */
   [0x2cf1, 0x2cf1], /* COPTIC COMBINING SPIRITUS LENIS */
-  [0x2cf2, 0x2cf2],
-  [0x2cf3, 0x2cf3],
+  [0x2cf2, 0x2cf3], /* COPTIC CAPITAL LETTER BOHAIRIC KHEI */
+  [0x2cf2, 0x2cf3], /* COPTIC SMALL LETTER BOHAIRIC KHEI */
   [0x2cf4, 0x2cf4],
   [0x2cf5, 0x2cf5],
   [0x2cf6, 0x2cf6],
@@ -11564,13 +11564,13 @@ var mapping = [
   [0x10c4, 0x2d24], /* GEORGIAN SMALL LETTER HAR */
   [0x10c5, 0x2d25], /* GEORGIAN SMALL LETTER HOE */
   [0x2d26, 0x2d26],
-  [0x2d27, 0x2d27],
+  [0x10c7, 0x2d27], /* GEORGIAN SMALL LETTER YN */
   [0x2d28, 0x2d28],
   [0x2d29, 0x2d29],
   [0x2d2a, 0x2d2a],
   [0x2d2b, 0x2d2b],
   [0x2d2c, 0x2d2c],
-  [0x2d2d, 0x2d2d],
+  [0x10cd, 0x2d2d], /* GEORGIAN SMALL LETTER AEN */
   [0x2d2e, 0x2d2e],
   [0x2d2f, 0x2d2f],
   [0x2d30, 0x2d30], /* TIFINAGH LETTER YA */
@@ -11627,8 +11627,8 @@ var mapping = [
   [0x2d63, 0x2d63], /* TIFINAGH LETTER YAZ */
   [0x2d64, 0x2d64], /* TIFINAGH LETTER TAWELLEMET YAZ */
   [0x2d65, 0x2d65], /* TIFINAGH LETTER YAZZ */
-  [0x2d66, 0x2d66],
-  [0x2d67, 0x2d67],
+  [0x2d66, 0x2d66], /* TIFINAGH LETTER YE */
+  [0x2d67, 0x2d67], /* TIFINAGH LETTER YO */
   [0x2d68, 0x2d68],
   [0x2d69, 0x2d69],
   [0x2d6a, 0x2d6a],
@@ -11831,16 +11831,16 @@ var mapping = [
   [0x2e2f, 0x2e2f], /* VERTICAL TILDE */
   [0x2e30, 0x2e30], /* RING POINT */
   [0x2e31, 0x2e31], /* WORD SEPARATOR MIDDLE DOT */
-  [0x2e32, 0x2e32],
-  [0x2e33, 0x2e33],
-  [0x2e34, 0x2e34],
-  [0x2e35, 0x2e35],
-  [0x2e36, 0x2e36],
-  [0x2e37, 0x2e37],
-  [0x2e38, 0x2e38],
-  [0x2e39, 0x2e39],
-  [0x2e3a, 0x2e3a],
-  [0x2e3b, 0x2e3b],
+  [0x2e32, 0x2e32], /* TURNED COMMA */
+  [0x2e33, 0x2e33], /* RAISED DOT */
+  [0x2e34, 0x2e34], /* RAISED COMMA */
+  [0x2e35, 0x2e35], /* TURNED SEMICOLON */
+  [0x2e36, 0x2e36], /* DAGGER WITH LEFT GUARD */
+  [0x2e37, 0x2e37], /* DAGGER WITH RIGHT GUARD */
+  [0x2e38, 0x2e38], /* TURNED DAGGER */
+  [0x2e39, 0x2e39], /* TOP HALF SECTION SIGN */
+  [0x2e3a, 0x2e3a], /* TWO-EM DASH */
+  [0x2e3b, 0x2e3b], /* THREE-EM DASH */
   [0x2e3c, 0x2e3c],
   [0x2e3d, 0x2e3d],
   [0x2e3e, 0x2e3e],
@@ -40913,7 +40913,7 @@ var mapping = [
   [0x9fc9, 0x9fc9], /* CJK Ideograph */
   [0x9fca, 0x9fca], /* CJK Ideograph */
   [0x9fcb, 0x9fcb], /* CJK Ideograph */
-  [0x9fcc, 0x9fcc],
+  [0x9fcc, 0x9fcc], /* CJK Ideograph */
   [0x9fcd, 0x9fcd],
   [0x9fce, 0x9fce],
   [0x9fcf, 0x9fcf],
@@ -42617,14 +42617,14 @@ var mapping = [
   [0xa671, 0xa671], /* COMBINING CYRILLIC HUNDRED MILLIONS SIGN */
   [0xa672, 0xa672], /* COMBINING CYRILLIC THOUSAND MILLIONS SIGN */
   [0xa673, 0xa673], /* SLAVONIC ASTERISK */
-  [0xa674, 0xa674],
-  [0xa675, 0xa675],
-  [0xa676, 0xa676],
-  [0xa677, 0xa677],
-  [0xa678, 0xa678],
-  [0xa679, 0xa679],
-  [0xa67a, 0xa67a],
-  [0xa67b, 0xa67b],
+  [0xa674, 0xa674], /* COMBINING CYRILLIC LETTER UKRAINIAN IE */
+  [0xa675, 0xa675], /* COMBINING CYRILLIC LETTER I */
+  [0xa676, 0xa676], /* COMBINING CYRILLIC LETTER YI */
+  [0xa677, 0xa677], /* COMBINING CYRILLIC LETTER U */
+  [0xa678, 0xa678], /* COMBINING CYRILLIC LETTER HARD SIGN */
+  [0xa679, 0xa679], /* COMBINING CYRILLIC LETTER YERU */
+  [0xa67a, 0xa67a], /* COMBINING CYRILLIC LETTER SOFT SIGN */
+  [0xa67b, 0xa67b], /* COMBINING CYRILLIC LETTER OMEGA */
   [0xa67c, 0xa67c], /* COMBINING CYRILLIC KAVYKA */
   [0xa67d, 0xa67d], /* COMBINING CYRILLIC PAYEROK */
   [0xa67e, 0xa67e], /* CYRILLIC KAVYKA */
@@ -42660,7 +42660,7 @@ var mapping = [
   [0xa69c, 0xa69c],
   [0xa69d, 0xa69d],
   [0xa69e, 0xa69e],
-  [0xa69f, 0xa69f],
+  [0xa69f, 0xa69f], /* COMBINING CYRILLIC LETTER IOTIFIED E */
   [0xa6a0, 0xa6a0], /* BAMUM LETTER A */
   [0xa6a1, 0xa6a1], /* BAMUM LETTER KA */
   [0xa6a2, 0xa6a2], /* BAMUM LETTER U */
@@ -42903,8 +42903,8 @@ var mapping = [
   [0xa78f, 0xa78f],
   [0xa790, 0xa791], /* LATIN CAPITAL LETTER N WITH DESCENDER */
   [0xa790, 0xa791], /* LATIN SMALL LETTER N WITH DESCENDER */
-  [0xa792, 0xa792],
-  [0xa793, 0xa793],
+  [0xa792, 0xa793], /* LATIN CAPITAL LETTER C WITH BAR */
+  [0xa792, 0xa793], /* LATIN SMALL LETTER C WITH BAR */
   [0xa794, 0xa794],
   [0xa795, 0xa795],
   [0xa796, 0xa796],
@@ -42927,7 +42927,7 @@ var mapping = [
   [0xa7a6, 0xa7a7], /* LATIN SMALL LETTER R WITH OBLIQUE STROKE */
   [0xa7a8, 0xa7a9], /* LATIN CAPITAL LETTER S WITH OBLIQUE STROKE */
   [0xa7a8, 0xa7a9], /* LATIN SMALL LETTER S WITH OBLIQUE STROKE */
-  [0xa7aa, 0xa7aa],
+  [0xa7aa, 0x266], /* LATIN CAPITAL LETTER H WITH HOOK */
   [0xa7ab, 0xa7ab],
   [0xa7ac, 0xa7ac],
   [0xa7ad, 0xa7ad],
@@ -43005,8 +43005,8 @@ var mapping = [
   [0xa7f5, 0xa7f5],
   [0xa7f6, 0xa7f6],
   [0xa7f7, 0xa7f7],
-  [0xa7f8, 0xa7f8],
-  [0xa7f9, 0xa7f9],
+  [0xa7f8, 0xa7f8], /* MODIFIER LETTER CAPITAL H WITH STROKE */
+  [0xa7f9, 0xa7f9], /* MODIFIER LETTER SMALL LIGATURE OE */
   [0xa7fa, 0xa7fa], /* LATIN LETTER SMALL CAPITAL TURNED M */
   [0xa7fb, 0xa7fb], /* LATIN EPIGRAPHIC LETTER REVERSED F */
   [0xa7fc, 0xa7fc], /* LATIN EPIGRAPHIC LETTER REVERSED P */
@@ -43749,29 +43749,29 @@ var mapping = [
   [0xaadd, 0xaadd], /* TAI VIET SYMBOL SAM */
   [0xaade, 0xaade], /* TAI VIET SYMBOL HO HOI */
   [0xaadf, 0xaadf], /* TAI VIET SYMBOL KOI KOI */
-  [0xaae0, 0xaae0],
-  [0xaae1, 0xaae1],
-  [0xaae2, 0xaae2],
-  [0xaae3, 0xaae3],
-  [0xaae4, 0xaae4],
-  [0xaae5, 0xaae5],
-  [0xaae6, 0xaae6],
-  [0xaae7, 0xaae7],
-  [0xaae8, 0xaae8],
-  [0xaae9, 0xaae9],
-  [0xaaea, 0xaaea],
-  [0xaaeb, 0xaaeb],
-  [0xaaec, 0xaaec],
-  [0xaaed, 0xaaed],
-  [0xaaee, 0xaaee],
-  [0xaaef, 0xaaef],
-  [0xaaf0, 0xaaf0],
-  [0xaaf1, 0xaaf1],
-  [0xaaf2, 0xaaf2],
-  [0xaaf3, 0xaaf3],
-  [0xaaf4, 0xaaf4],
-  [0xaaf5, 0xaaf5],
-  [0xaaf6, 0xaaf6],
+  [0xaae0, 0xaae0], /* MEETEI MAYEK LETTER E */
+  [0xaae1, 0xaae1], /* MEETEI MAYEK LETTER O */
+  [0xaae2, 0xaae2], /* MEETEI MAYEK LETTER CHA */
+  [0xaae3, 0xaae3], /* MEETEI MAYEK LETTER NYA */
+  [0xaae4, 0xaae4], /* MEETEI MAYEK LETTER TTA */
+  [0xaae5, 0xaae5], /* MEETEI MAYEK LETTER TTHA */
+  [0xaae6, 0xaae6], /* MEETEI MAYEK LETTER DDA */
+  [0xaae7, 0xaae7], /* MEETEI MAYEK LETTER DDHA */
+  [0xaae8, 0xaae8], /* MEETEI MAYEK LETTER NNA */
+  [0xaae9, 0xaae9], /* MEETEI MAYEK LETTER SHA */
+  [0xaaea, 0xaaea], /* MEETEI MAYEK LETTER SSA */
+  [0xaaeb, 0xaaeb], /* MEETEI MAYEK VOWEL SIGN II */
+  [0xaaec, 0xaaec], /* MEETEI MAYEK VOWEL SIGN UU */
+  [0xaaed, 0xaaed], /* MEETEI MAYEK VOWEL SIGN AAI */
+  [0xaaee, 0xaaee], /* MEETEI MAYEK VOWEL SIGN AU */
+  [0xaaef, 0xaaef], /* MEETEI MAYEK VOWEL SIGN AAU */
+  [0xaaf0, 0xaaf0], /* MEETEI MAYEK CHEIKHAN */
+  [0xaaf1, 0xaaf1], /* MEETEI MAYEK AHANG KHUDAM */
+  [0xaaf2, 0xaaf2], /* MEETEI MAYEK ANJI */
+  [0xaaf3, 0xaaf3], /* MEETEI MAYEK SYLLABLE REPETITION MARK */
+  [0xaaf4, 0xaaf4], /* MEETEI MAYEK WORD REPETITION MARK */
+  [0xaaf5, 0xaaf5], /* MEETEI MAYEK VOWEL SIGN VISARGA */
+  [0xaaf6, 0xaaf6], /* MEETEI MAYEK VIRAMA */
   [0xaaf7, 0xaaf7],
   [0xaaf8, 0xaaf8],
   [0xaaf9, 0xaaf9],
@@ -64051,8 +64051,8 @@ var mapping = [
   [0xfa2b, 0xfa2b], /* CJK COMPATIBILITY IDEOGRAPH-FA2B */
   [0xfa2c, 0xfa2c], /* CJK COMPATIBILITY IDEOGRAPH-FA2C */
   [0xfa2d, 0xfa2d], /* CJK COMPATIBILITY IDEOGRAPH-FA2D */
-  [0xfa2e, 0xfa2e],
-  [0xfa2f, 0xfa2f],
+  [0xfa2e, 0xfa2e], /* CJK COMPATIBILITY IDEOGRAPH-FA2E */
+  [0xfa2f, 0xfa2f], /* CJK COMPATIBILITY IDEOGRAPH-FA2F */
   [0xfa30, 0xfa30], /* CJK COMPATIBILITY IDEOGRAPH-FA30 */
   [0xfa31, 0xfa31], /* CJK COMPATIBILITY IDEOGRAPH-FA31 */
   [0xfa32, 0xfa32], /* CJK COMPATIBILITY IDEOGRAPH-FA32 */
diff --git a/js/src/tests/js1_5/Regress/regress-343713.js b/js/src/tests/js1_5/Regress/regress-343713.js
index 0b2082a32728..1320a7774a83 100644
--- a/js/src/tests/js1_5/Regress/regress-343713.js
+++ b/js/src/tests/js1_5/Regress/regress-343713.js
@@ -37,7 +37,7 @@
 
 //-----------------------------------------------------------------------------
 var BUGNUMBER = 343713;
-var summary = 'Do not JS_Assert with nested function evaluation';
+var summary = 'Do not assert with nested function evaluation';
 var actual = 'No Crash';
 var expect = 'No Crash';
 
diff --git a/js/src/vm/Unicode.cpp b/js/src/vm/Unicode.cpp
index 55e335752416..8d92a0678262 100644
--- a/js/src/vm/Unicode.cpp
+++ b/js/src/vm/Unicode.cpp
@@ -111,6 +111,7 @@ const CharacterInfo js_charinfo[] = {
     {65333, 0, 2},
     {65329, 0, 2},
     {42893, 613, 10},
+    {42922, 614, 10},
     {65327, 0, 2},
     {65325, 0, 2},
     {10743, 0, 2},
@@ -198,23 +199,23 @@ const CharacterInfo js_charinfo[] = {
 
 const uint8_t index1[] = {
       0,   1,   2,   3,   4,   5,   6,   7,   8,   9,  10,  11,  12,  13,  14,  15,  16,  17,
-     18,  19,  20,  21,  22,  23,  24,  25,  26,  27,  28,  29,  30,  31,  32,  33,  34,  34,
-     35,  36,  37,  38,  39,  40,  41,  42,  43,  44,  45,  46,  47,  48,  49,  50,  51,  52,
-     53,  54,  55,  56,  57,  58,  59,  60,  61,  62,  63,  64,  65,  66,  26,  26,  26,  26,
-     26,  67,  68,  69,  70,  71,  72,  73,  74,  26,  26,  26,  26,  26,  26,  26,  26,  75,
-     76,  77,  78,  79,  80,  81,  82,  83,  84,  85,  86,  87,  88,  89,  90,  91,  92,  34,
-     93,  94,  95,  96,  97,  98,  34,  99,  26, 100,  26, 101, 102, 102, 103, 102, 104, 105,
-    106, 107, 108, 109, 110, 111, 112, 113, 114,  34,  34,  34,  34,  34,  34,  34,  34,  34,
-     34,  34, 115, 116,  34,  34,  34,  34,  34,  34,  34,  34,  34,  34,  34,  34,  34,  34,
-     34,  34,  34,  34,  34,  34,  34,  34,  34,  34,  34,  34,  34,  34, 117, 118, 102, 119,
-    120, 121, 122, 123, 124,  34,  34,  34,  34,  34,  34,  34, 125,  74, 126, 127, 128,  26,
-    129, 130,  34,  34,  34,  34,  34,  34,  34,  34,  26,  26,  26,  26,  26,  26,  26,  26,
+     18,  19,  20,  21,  22,  23,  24,  25,  26,  27,  28,  29,  30,  31,  32,  33,  34,  35,
+     36,  37,  38,  39,  40,  41,  42,  43,  44,  45,  46,  47,  48,  49,  50,  51,  52,  53,
+     54,  55,  56,  57,  58,  59,  60,  61,  62,  63,  64,  65,  66,  67,  26,  26,  26,  26,
+     26,  68,  69,  70,  71,  72,  73,  74,  75,  26,  26,  26,  26,  26,  26,  26,  26,  76,
+     77,  78,  79,  80,  81,  82,  83,  84,  85,  86,  87,  88,  89,  90,  91,  92,  93,  94,
+     95,  96,  97,  98,  99, 100,  94, 101,  26, 102,  26, 103, 104, 104, 105, 104, 106, 107,
+    108, 109, 110, 111, 112, 113, 114, 115, 116,  94,  94,  94,  94,  94,  94,  94,  94,  94,
+     94,  94, 117, 118,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,
+     94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94, 119, 120, 104, 121,
+    122, 123, 124, 125, 126,  94,  94,  94,  94,  94,  94,  94, 127,  75, 128, 129, 130,  26,
+    131, 132,  94,  94,  94,  94,  94,  94,  94,  94,  26,  26,  26,  26,  26,  26,  26,  26,
      26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,
      26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,
      26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,
      26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,
      26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,
-     26,  26,  26,  26,  85,  34,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,
+     26,  26,  26,  26,  86,  94,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,
      26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,
      26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,
      26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,
@@ -232,10 +233,10 @@ const uint8_t index1[] = {
      26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,
      26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,
      26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,
-     26,  26,  26,  26,  26,  26,  26,  26,  26, 131,  26,  26,  26,  26,  26,  26,  26,  26,
-     26,  26,  26,  26,  26,  26,  26,  26,  26,  26, 132, 133,  26,  26,  26,  26, 134, 135,
-    136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153,
-    154,  34,  34, 155,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,
+     26,  26,  26,  26,  26,  26,  26,  26,  26, 133,  26,  26,  26,  26,  26,  26,  26,  26,
+     26,  26,  26,  26,  26,  26,  26,  26,  26,  26, 133, 134,  26,  26,  26,  26, 135, 136,
+    137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154,
+    155,  94,  94, 156,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,
      26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,
      26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,
      26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,
@@ -244,16 +245,16 @@ const uint8_t index1[] = {
      26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,
      26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,
      26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,
-     26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26, 156, 157,
-     34,  34,  34,  34,  34,  34,  34,  34,  34,  34,  34,  34,  34,  34,  34,  34,  34,  34,
-     34,  34,  34,  34,  34,  34,  34,  34,  34,  34,  34,  34,  34,  34,  34,  34,  34,  34,
-     34,  34,  34,  34,  34,  34,  34,  34,  34,  34,  34,  34,  34,  34,  34,  34,  34,  34,
-     34,  34,  34,  34,  34,  34,  34,  34,  34,  34,  34,  34,  34,  34,  34,  34,  34,  34,
-     34,  34,  34,  34,  34,  34,  34,  34,  34,  34,  34,  34,  34,  34,  34,  34,  34,  34,
-     34,  34,  34,  34,  34,  34,  34,  34,  34,  34,  34,  34,  34,  34,  34,  34,  34,  34,
-     34,  34,  34,  34,  34,  34,  34,  34,  34,  34,  34,  34,  34,  34,  34,  34,  34,  34,
-     34,  34,  34,  34,  34,  34,  26,  26,  26,  26, 158, 158,  26, 159, 160, 161, 162, 163,
-     26,  26,  26,  26, 164, 165, 166, 167, 168, 169,  26, 170, 171, 172, 173, 174,
+     26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26, 157, 158,
+     94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,
+     94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,
+     94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,
+     94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,
+     94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,
+     94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,
+     94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,
+     94,  94,  94,  94,  94,  94,  26,  26,  26,  26,  26, 159,  26, 160, 161, 162, 163, 164,
+     26,  26,  26,  26, 165, 166, 167, 168, 169, 170,  26, 171, 172, 173, 174, 175,
 };
 
 const uint8_t index2[] = {
@@ -291,9 +292,9 @@ const uint8_t index2[] = {
       8,   9,   8,   9,   8,   9,   5,   5,   5,   5,   5,   5,  40,   8,   9,  41,  42,  43,
      43,   8,   9,  44,  45,  46,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,  47,  48,
      49,  50,  51,   5,  52,  52,   5,  53,   5,  54,   5,   5,   5,   5,  52,   5,   5,  55,
-      5,  56,   5,   5,  57,  58,   5,  59,   5,   5,   5,  58,   5,  60,  61,   5,   5,  62,
-      5,   5,   5,   5,   5,   5,   5,  63,   5,   5,  64,   5,   5,  64,   5,   5,   5,   5,
-     64,  65,  66,  66,  67,   5,   5,   5,   5,   5,  68,   5,   5,   5,   5,   5,   5,   5,
+      5,  56,  57,   5,  58,  59,   5,  60,   5,   5,   5,  59,   5,  61,  62,   5,   5,  63,
+      5,   5,   5,   5,   5,   5,   5,  64,   5,   5,  65,   5,   5,  65,   5,   5,   5,   5,
+     65,  66,  67,  67,  68,   5,   5,   5,   5,   5,  69,   5,   5,   5,   5,   5,   5,   5,
       5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
       5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
       5,   5,   5,   5,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
@@ -303,39 +304,39 @@ const uint8_t index2[] = {
       2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,
       2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,
       2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,
-      2,   2,   2,   2,   2,   2,   2,   2,   2,  69,   2,   2,   2,   2,   2,   2,   2,   2,
+      2,   2,   2,   2,   2,   2,   2,   2,   2,  70,   2,   2,   2,   2,   2,   2,   2,   2,
       2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,
       2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   8,   9,
       8,   9,   5,   0,   8,   9,   0,   0,   5,  27,  27,  27,   0,   0,   0,   0,   0,   0,
-      0,   0,  70,   0,  71,  71,  71,   0,  72,   0,  73,  73,   5,   3,   3,   3,   3,   3,
+      0,   0,  71,   0,  72,  72,  72,   0,  73,   0,  74,  74,   5,   3,   3,   3,   3,   3,
       3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   0,   3,   3,   3,   3,   3,
-      3,   3,   3,   3,  74,  75,  75,  75,   5,   4,   4,   4,   4,   4,   4,   4,   4,   4,
-      4,   4,   4,   4,   4,   4,   4,   4,  76,   4,   4,   4,   4,   4,   4,   4,   4,   4,
-     77,  78,  78,  79,  80,  81,   5,   5,   5,  82,  83,  84,   8,   9,   8,   9,   8,   9,
+      3,   3,   3,   3,  75,  76,  76,  76,   5,   4,   4,   4,   4,   4,   4,   4,   4,   4,
+      4,   4,   4,   4,   4,   4,   4,   4,  77,   4,   4,   4,   4,   4,   4,   4,   4,   4,
+     78,  79,  79,  80,  81,  82,   5,   5,   5,  83,  84,  85,   8,   9,   8,   9,   8,   9,
       8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,
-     85,  86,  87,   5,  88,  89,   0,   8,   9,  90,   8,   9,   5,  39,  39,  39,  91,  91,
-     91,  91,  91,  91,  91,  91,  91,  91,  91,  91,  91,  91,  91,  91,   3,   3,   3,   3,
+     86,  87,  88,   5,  89,  90,   0,   8,   9,  91,   8,   9,   5,  39,  39,  39,  92,  92,
+     92,  92,  92,  92,  92,  92,  92,  92,  92,  92,  92,  92,  92,  92,   3,   3,   3,   3,
       3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,
       3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   4,   4,   4,   4,   4,   4,   4,   4,
       4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,
-      4,   4,   4,   4,   4,   4,  86,  86,  86,  86,  86,  86,  86,  86,  86,  86,  86,  86,
-     86,  86,  86,  86,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,
+      4,   4,   4,   4,   4,   4,  87,  87,  87,  87,  87,  87,  87,  87,  87,  87,  87,  87,
+     87,  87,  87,  87,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,
       8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,
-      8,   9,   0,   2,   2,   2,   2,   2,  92,  92,   8,   9,   8,   9,   8,   9,   8,   9,
+      8,   9,   0,   2,   2,   2,   2,   2,  93,  93,   8,   9,   8,   9,   8,   9,   8,   9,
       8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,
       8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,
-      8,   9,   8,   9,   8,   9,   8,   9,   8,   9,  93,   8,   9,   8,   9,   8,   9,   8,
-      9,   8,   9,   8,   9,   8,   9,  94,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,
+      8,   9,   8,   9,   8,   9,   8,   9,   8,   9,  94,   8,   9,   8,   9,   8,   9,   8,
+      9,   8,   9,   8,   9,   8,   9,  95,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,
       8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,
       8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,
       8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,
       8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,
-      8,   9,   8,   9,   8,   9,   0,   0,   0,   0,   0,   0,   0,   0,   0,  95,  95,  95,
-     95,  95,  95,  95,  95,  95,  95,  95,  95,  95,  95,  95,  95,  95,  95,  95,  95,  95,
-     95,  95,  95,  95,  95,  95,  95,  95,  95,  95,  95,  95,  95,  95,  95,  95,  95,   0,
-      0,   5,   0,   0,   0,   0,   0,   0,   0,  96,  96,  96,  96,  96,  96,  96,  96,  96,
+      8,   9,   8,   9,   8,   9,   0,   0,   0,   0,   0,   0,   0,   0,   0,  96,  96,  96,
      96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,
-     96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,   5,   0,   0,   0,   0,   0,   0,
+     96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,   0,
+      0,   5,   0,   0,   0,   0,   0,   0,   0,  97,  97,  97,  97,  97,  97,  97,  97,  97,
+     97,  97,  97,  97,  97,  97,  97,  97,  97,  97,  97,  97,  97,  97,  97,  97,  97,  97,
+     97,  97,  97,  97,  97,  97,  97,  97,  97,  97,  97,   5,   0,   0,   0,   0,   0,   0,
       0,   0,   0,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,
       2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,
       2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   0,   2,   0,   2,   2,   0,
@@ -379,507 +380,510 @@ const uint8_t index2[] = {
       0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
       0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
       0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   0,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
       0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
       0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   2,   2,   2,  97,   5,   5,   5,   5,   5,   5,
+      0,   0,   0,   0,   0,   0,   0,   0,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,
+      2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   0,
+      2,   2,   2,  98,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
       5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
       5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   2,  97,   2,   5,  97,  97,
-     97,   2,   2,   2,   2,   2,   2,   2,   2,  97,  97,  97,  97,   2,  97,  97,   5,   2,
-      2,   2,   2,   2,   2,   2,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   2,   2,
-      0,   0,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   0,   5,   5,   5,   5,   5,
-      5,   5,   0,   5,   5,   5,   5,   5,   5,   5,   0,   2,  97,  97,   0,   5,   5,   5,
-      5,   5,   5,   5,   5,   0,   0,   5,   5,   0,   0,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   5,   5,
-      5,   5,   5,   5,   5,   0,   5,   0,   0,   0,   5,   5,   5,   5,   0,   0,   2,   5,
-     97,  97,  97,   2,   2,   2,   2,   0,   0,  97,  97,   0,   0,  97,  97,   2,   5,   0,
-      0,   0,   0,   0,   0,   0,   0,  97,   0,   0,   0,   0,   5,   5,   0,   5,   5,   5,
-      2,   2,   0,   0,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   5,   5,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   2,   2,  97,   0,   5,
-      5,   5,   5,   5,   5,   0,   0,   0,   0,   5,   5,   0,   0,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   2,  98,   2,   5,  98,  98,  98,   2,   2,   2,   2,   2,   2,   2,
+      2,  98,  98,  98,  98,   2,  98,  98,   5,   2,   2,   2,   2,   2,   2,   2,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   2,   2,   0,   0,   2,   2,   2,   2,   2,   2,
+      2,   2,   2,   2,   0,   5,   5,   5,   5,   5,   5,   5,   0,   5,   5,   5,   5,   5,
+      5,   5,   0,   2,  98,  98,   0,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,   5,
+      5,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   0,   5,   5,   5,   5,   5,   5,   5,   0,   5,   0,
+      0,   0,   5,   5,   5,   5,   0,   0,   2,   5,  98,  98,  98,   2,   2,   2,   2,   0,
+      0,  98,  98,   0,   0,  98,  98,   2,   5,   0,   0,   0,   0,   0,   0,   0,   0,  98,
+      0,   0,   0,   0,   5,   5,   0,   5,   5,   5,   2,   2,   0,   0,   2,   2,   2,   2,
+      2,   2,   2,   2,   2,   2,   5,   5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   2,   2,  98,   0,   5,   5,   5,   5,   5,   5,   0,   0,   0,
+      0,   5,   5,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   5,   5,   5,   5,   5,   5,   5,   0,
+      5,   5,   0,   5,   5,   0,   5,   5,   0,   0,   2,   0,  98,  98,  98,   2,   2,   0,
+      0,   0,   0,   2,   2,   0,   0,   2,   2,   2,   0,   0,   0,   2,   0,   0,   0,   0,
+      0,   0,   0,   5,   5,   5,   5,   0,   5,   0,   0,   0,   0,   0,   0,   0,   2,   2,
+      2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   5,   5,   5,   2,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   2,   2,  98,   0,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   0,   5,   5,   5,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   5,   5,   5,   5,   5,   5,
+      5,   0,   5,   5,   0,   5,   5,   5,   5,   5,   0,   0,   2,   5,  98,  98,  98,   2,
+      2,   2,   2,   2,   0,   2,   2,  98,   0,  98,  98,   2,   0,   0,   5,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,   2,   2,   0,   0,
+      2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   2,  98,  98,   0,   5,   5,   5,   5,   5,
+      5,   5,   5,   0,   0,   5,   5,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   5,   5,   5,   5,
+      5,   5,   5,   0,   5,   5,   0,   5,   5,   5,   5,   5,   0,   0,   2,   5,  98,   2,
+     98,   2,   2,   2,   2,   0,   0,  98,  98,   0,   0,  98,  98,   2,   0,   0,   0,   0,
+      0,   0,   0,   0,   2,  98,   0,   0,   0,   0,   5,   5,   0,   5,   5,   5,   2,   2,
+      0,   0,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   0,   5,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   2,   5,   0,   5,   5,   5,
+      5,   5,   5,   0,   0,   0,   5,   5,   5,   0,   5,   5,   5,   5,   0,   0,   0,   5,
+      5,   0,   5,   0,   5,   5,   0,   0,   0,   5,   5,   0,   0,   0,   5,   5,   5,   0,
+      0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,   0,   0,
+     98,  98,   2,  98,  98,   0,   0,   0,  98,  98,  98,   0,  98,  98,  98,   2,   0,   0,
+      5,   0,   0,   0,   0,   0,   0,  98,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,  98,  98,  98,   0,   5,
+      5,   5,   5,   5,   5,   5,   5,   0,   5,   5,   5,   0,   5,   5,   5,   5,   5,   5,
       5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,
-      5,   5,   5,   5,   5,   5,   5,   0,   5,   5,   0,   5,   5,   0,   5,   5,   0,   0,
-      2,   0,  97,  97,  97,   2,   2,   0,   0,   0,   0,   2,   2,   0,   0,   2,   2,   2,
-      0,   0,   0,   2,   0,   0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   0,   5,   0,
-      0,   0,   0,   0,   0,   0,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,
-      5,   5,   5,   2,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   2,   2,  97,
-      0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   5,   5,   5,   0,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   5,   5,   5,   5,   5,   0,   0,
+      0,   5,   2,   2,   2,  98,  98,  98,  98,   0,   2,   2,   2,   0,   2,   2,   2,   2,
+      0,   0,   0,   0,   0,   0,   0,   2,   2,   0,   5,   5,   0,   0,   0,   0,   0,   0,
+      5,   5,   2,   2,   0,   0,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,  98,  98,
+      0,   5,   5,   5,   5,   5,   5,   5,   5,   0,   5,   5,   5,   0,   5,   5,   5,   5,
       5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   0,   5,   5,   5,   5,   5,   5,   5,   0,   5,   5,   0,   5,   5,   5,   5,   5,
-      0,   0,   2,   5,  97,  97,  97,   2,   2,   2,   2,   2,   0,   2,   2,  97,   0,  97,
-     97,   2,   0,   0,   5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   5,   5,   2,   2,   0,   0,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   2,
-     97,  97,   0,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,   5,   5,   0,   0,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   0,   5,   5,   5,   5,   5,   5,   5,   0,   5,   5,   0,   5,   5,   5,
-      5,   5,   0,   0,   2,   5,  97,   2,  97,   2,   2,   2,   2,   0,   0,  97,  97,   0,
-      0,  97,  97,   2,   0,   0,   0,   0,   0,   0,   0,   0,   2,  97,   0,   0,   0,   0,
-      5,   5,   0,   5,   5,   5,   2,   2,   0,   0,   2,   2,   2,   2,   2,   2,   2,   2,
-      2,   2,   0,   5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   2,   5,   0,   5,   5,   5,   5,   5,   5,   0,   0,   0,   5,   5,   5,   0,
-      5,   5,   5,   5,   0,   0,   0,   5,   5,   0,   5,   0,   5,   5,   0,   0,   0,   5,
-      5,   0,   0,   0,   5,   5,   5,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   0,   0,   0,   0,  97,  97,   2,  97,  97,   0,   0,   0,  97,  97,
-     97,   0,  97,  97,  97,   2,   0,   0,   5,   0,   0,   0,   0,   0,   0,  97,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   2,   2,   2,   2,   2,   2,
-      2,   2,   2,   2,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,  97,  97,  97,   0,   5,   5,   5,   5,   5,   5,   5,   5,   0,   5,   5,
-      5,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      0,   5,   5,   5,   5,   5,   0,   0,   0,   5,   2,   2,   2,  97,  97,  97,  97,   0,
-      2,   2,   2,   0,   2,   2,   2,   2,   0,   0,   0,   0,   0,   0,   0,   2,   2,   0,
-      5,   5,   0,   0,   0,   0,   0,   0,   5,   5,   2,   2,   0,   0,   2,   2,   2,   2,
-      2,   2,   2,   2,   2,   2,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,  97,  97,   0,   5,   5,   5,   5,   5,   5,   5,   5,   0,
-      5,   5,   5,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   0,   5,   5,   5,   5,   5,   0,   0,   2,   5,  97,   2,  97,  97,  97,  97,
-     97,   0,   2,  97,  97,   0,  97,  97,   2,   2,   0,   0,   0,   0,   0,   0,   0,  97,
-     97,   0,   0,   0,   0,   0,   0,   0,   5,   0,   5,   5,   2,   2,   0,   0,   2,   2,
-      2,   2,   2,   2,   2,   2,   2,   2,   0,   5,   5,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,  97,  97,   0,   5,   5,   5,   5,   5,   5,   5,
-      5,   0,   5,   5,   5,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,   5,  97,  97,  97,   2,
-      2,   2,   2,   0,  97,  97,  97,   0,  97,  97,  97,   2,   5,   0,   0,   0,   0,   0,
-      0,   0,   0,  97,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,   2,   2,   0,   0,
-      2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   5,   5,   5,   5,   5,   5,   0,   0,  97,  97,   0,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,   0,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   5,   0,   0,
-      5,   5,   5,   5,   5,   5,   5,   0,   0,   0,   2,   0,   0,   0,   0,  97,  97,  97,
-      2,   2,   2,   0,   2,   0,  97,  97,  97,  97,  97,  97,  97,  97,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,  97,  97,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,
+      5,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   5,   5,   5,   5,   5,
+      0,   0,   2,   5,  98,   2,  98,  98,  98,  98,  98,   0,   2,  98,  98,   0,  98,  98,
+      2,   2,   0,   0,   0,   0,   0,   0,   0,  98,  98,   0,   0,   0,   0,   0,   0,   0,
+      5,   0,   5,   5,   2,   2,   0,   0,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,
+      0,   5,   5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+     98,  98,   0,   5,   5,   5,   5,   5,   5,   5,   5,   0,   5,   5,   5,   0,   5,   5,
       5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
       5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   2,   5,   5,   2,   2,   2,   2,   2,   2,   2,   0,   0,   0,
-      0,   0,   5,   5,   5,   5,   5,   5,   5,   2,   2,   2,   2,   2,   2,   2,   2,   0,
-      2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   0,   0,   0,   0,   0,   0,   0,   0,
+      5,   5,   5,   0,   0,   5,  98,  98,  98,   2,   2,   2,   2,   0,  98,  98,  98,   0,
+     98,  98,  98,   2,   5,   0,   0,   0,   0,   0,   0,   0,   0,  98,   0,   0,   0,   0,
+      0,   0,   0,   0,   5,   5,   2,   2,   0,   0,   2,   2,   2,   2,   2,   2,   2,   2,
+      2,   2,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,
+      0,   0,  98,  98,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   0,   5,   0,   0,   5,   5,   5,   5,   5,   5,   5,   0,
+      0,   0,   2,   0,   0,   0,   0,  98,  98,  98,   2,   2,   2,   0,   2,   0,  98,  98,
+     98,  98,  98,  98,  98,  98,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,  98,  98,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   2,   5,   5,
+      2,   2,   2,   2,   2,   2,   2,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,
+      5,   2,   2,   2,   2,   2,   2,   2,   2,   0,   2,   2,   2,   2,   2,   2,   2,   2,
+      2,   2,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
       0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,   0,   5,   0,
-      0,   5,   5,   0,   5,   0,   0,   5,   0,   0,   0,   0,   0,   0,   5,   5,   5,   5,
-      0,   5,   5,   5,   5,   5,   5,   5,   0,   5,   5,   5,   0,   5,   0,   5,   0,   0,
-      5,   5,   0,   5,   5,   5,   5,   2,   5,   5,   2,   2,   2,   2,   2,   2,   0,   2,
-      2,   5,   0,   0,   5,   5,   5,   5,   5,   0,   5,   0,   2,   2,   2,   2,   2,   2,
-      0,   0,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   0,   0,   5,   5,   0,   0,
+      0,   0,   0,   0,   0,   5,   5,   0,   5,   0,   0,   5,   5,   0,   5,   0,   0,   5,
+      0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   0,   5,   5,   5,   5,   5,   5,   5,
+      0,   5,   5,   5,   0,   5,   0,   5,   0,   0,   5,   5,   0,   5,   5,   5,   5,   2,
+      5,   5,   2,   2,   2,   2,   2,   2,   0,   2,   2,   5,   0,   0,   5,   5,   5,   5,
+      5,   0,   5,   0,   2,   2,   2,   2,   2,   2,   0,   0,   2,   2,   2,   2,   2,   2,
+      2,   2,   2,   2,   0,   0,   5,   5,   5,   5,   0,   0,   0,   0,   0,   0,   0,   0,
       0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   2,   2,   0,   0,   0,   0,   0,   0,   2,   2,   2,   2,   2,   2,   2,   2,
-      2,   2,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   2,   0,   2,   0,   2,
-      0,   0,   0,   0,  97,  97,   5,   5,   5,   5,   5,   5,   5,   5,   0,   5,   5,   5,
+      0,   0,   0,   0,   0,   0,   5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   2,   2,   0,   0,   0,   0,
+      0,   0,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   2,   0,   2,   0,   2,   0,   0,   0,   0,  98,  98,   5,   5,
+      5,   5,   5,   5,   5,   5,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
       5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,   0,
-      0,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,  97,   2,   2,
-      2,   2,   2,   0,   2,   2,   5,   5,   5,   5,   5,   2,   2,   2,   2,   2,   2,   2,
-      2,   2,   2,   2,   0,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,
+      5,   5,   5,   5,   5,   5,   5,   0,   0,   0,   0,   2,   2,   2,   2,   2,   2,   2,
+      2,   2,   2,   2,   2,   2,   2,  98,   2,   2,   2,   2,   2,   0,   2,   2,   5,   5,
+      5,   5,   5,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   0,   2,   2,   2,
       2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,
-      2,   2,   2,   2,   2,   0,   0,   0,   0,   0,   0,   0,   0,   0,   2,   0,   0,   0,
+      2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   2,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
       0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
       0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,  97,  97,   2,   2,   2,   2,  97,   2,   2,   2,   2,
-      2,   2,  97,   2,   2,  97,  97,   2,   2,   5,   2,   2,   2,   2,   2,   2,   2,   2,
-      2,   2,   0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,  97,  97,   2,   2,
-      5,   5,   5,   5,   2,   2,   2,   5,  97,  97,  97,   5,   5,  97,  97,  97,  97,  97,
-     97,  97,   5,   5,   5,   2,   2,   2,   2,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   2,  97,  97,   2,   2,  97,  97,  97,  97,  97,  97,   2,   5,  97,
-      2,   2,   2,   2,   2,   2,   2,   2,   2,   2,  97,  97,  97,   2,   0,   0,  98,  98,
-     98,  98,  98,  98,  98,  98,  98,  98,  98,  98,  98,  98,  98,  98,  98,  98,  98,  98,
-     98,  98,  98,  98,  98,  98,  98,  98,  98,  98,  98,  98,  98,  98,  98,  98,  98,  98,
       0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,
       5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,
-      5,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   5,   5,   5,   5,
-      0,   0,   5,   5,   5,   5,   5,   5,   5,   0,   5,   0,   5,   5,   5,   5,   0,   0,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,  98,
+     98,   2,   2,   2,   2,  98,   2,   2,   2,   2,   2,   2,  98,   2,   2,  98,  98,   2,
+      2,   5,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   0,   0,   0,   0,   0,   0,
+      5,   5,   5,   5,   5,   5,  98,  98,   2,   2,   5,   5,   5,   5,   2,   2,   2,   5,
+     98,  98,  98,   5,   5,  98,  98,  98,  98,  98,  98,  98,   5,   5,   5,   2,   2,   2,
+      2,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   2,  98,  98,   2,
+      2,  98,  98,  98,  98,  98,  98,   2,   5,  98,   2,   2,   2,   2,   2,   2,   2,   2,
+      2,   2,  98,  98,  98,   2,   0,   0,  99,  99,  99,  99,  99,  99,  99,  99,  99,  99,
+     99,  99,  99,  99,  99,  99,  99,  99,  99,  99,  99,  99,  99,  99,  99,  99,  99,  99,
+     99,  99,  99,  99,  99,  99,  99,  99,  99,  99,   0,  99,   0,   0,   0,   0,   0,  99,
+      0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
       5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   5,   5,   5,   5,   5,   5,   5,   5,
       5,   5,   5,   5,   5,   0,   5,   5,   5,   5,   0,   0,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   5,   5,   5,   5,   0,   0,   5,   5,
-      5,   5,   5,   5,   5,   0,   5,   0,   5,   5,   5,   5,   0,   0,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   0,   5,   0,   5,   5,   5,   5,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,
       5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
       5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   5,   5,
       5,   5,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
       5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   0,   5,   5,   5,   5,   0,   0,   5,   5,   5,   5,   5,   5,   5,   0,   5,   0,
+      5,   5,   5,   5,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   0,   5,   5,   5,   5,   0,   0,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,   2,   2,   2,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   1,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,   0,   5,   5,
+      5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   5,   5,   5,   5,   2,   2,
+      2,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   2,   2,   2,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   2,   2,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,
+      5,   5,   5,   0,   2,   2,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   2,   2,
+     98,   2,   2,   2,   2,   2,   2,   2,  98,  98,  98,  98,  98,  98,  98,  98,   2,  98,
+     98,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   0,   0,   0,   5,   0,   0,
+      0,   0,   5,   2,   0,   0,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   2,   2,   2,   1,   0,
+      2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   0,   0,   0,   0,   0,   0,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,   0,   0,
+      0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   2,   5,   0,   0,   0,   0,   0,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,
+      0,   0,   2,   2,   2,  98,  98,  98,  98,   2,   2,  98,  98,  98,   0,   0,   0,   0,
+     98,  98,   2,  98,  98,  98,  98,  98,  98,   2,   2,   2,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   0,   0,   5,   5,   5,   5,   5,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,
+      0,   0,  98,  98,  98,  98,  98,  98,  98,  98,  98,  98,  98,  98,  98,  98,  98,  98,
+     98,   5,   5,   5,   5,   5,   5,   5,  98,  98,   0,   0,   0,   0,   0,   0,   2,   2,
+      2,   2,   2,   2,   2,   2,   2,   2,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   2,   2,  98,
+     98,  98,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,  98,   2,  98,   2,   2,   2,   2,   2,   2,   2,   0,   2,  98,
+      2,  98,  98,   2,   2,   2,   2,   2,   2,   2,   2,  98,  98,  98,  98,  98,  98,   2,
+      2,   2,   2,   2,   2,   2,   2,   2,   2,   0,   0,   2,   2,   2,   2,   2,   2,   2,
+      2,   2,   2,   2,   0,   0,   0,   0,   0,   0,   2,   2,   2,   2,   2,   2,   2,   2,
+      2,   2,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   2,   2,   2,   2,
+     98,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   2,  98,   2,   2,   2,   2,
+      2,  98,   2,  98,  98,  98,  98,  98,   2,  98,  98,   5,   5,   5,   5,   5,   5,   5,
+      0,   0,   0,   0,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   2,   2,   2,   2,   2,
+      2,   2,   2,   2,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   2,   2,
+     98,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,  98,   2,   2,   2,   2,
+     98,  98,   2,   2,  98,   2,  98,  98,   5,   5,   2,   2,   2,   2,   2,   2,   2,   2,
+      2,   2,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   2,  98,   2,   2,  98,  98,  98,   2,
+     98,   2,   2,   2,  98,  98,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+     98,  98,  98,  98,  98,  98,  98,  98,   2,   2,   2,   2,   2,   2,   2,   2,  98,  98,
+      2,   2,   0,   0,   0,   0,   0,   0,   0,   0,   2,   2,   2,   2,   2,   2,   2,   2,
+      2,   2,   0,   0,   0,   5,   5,   5,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      2,   2,   2,   0,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,  98,
+      2,   2,   2,   2,   2,   2,   2,   5,   5,   5,   5,   2,   5,   5,   5,   5,  98,  98,
+      2,   5,   5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5, 100,   5,   5,
+      5, 101,   5,   5,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,
+      2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,
+      2,   2,   2,   2,   2,   2,   2,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   2,   2,   2,   2,   8,   9,   8,   9,
+      8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,
+      8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,
+      8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,
+      8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,
+      8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   5,   5,   5,   5,   5, 102,   5,   5,
+    103,   5,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,
+      8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9, 104, 104,
+    104, 104, 104, 104, 104, 104, 105, 105, 105, 105, 105, 105, 105, 105, 104, 104, 104, 104,
+    104, 104,   0,   0, 105, 105, 105, 105, 105, 105,   0,   0, 104, 104, 104, 104, 104, 104,
+    104, 104, 105, 105, 105, 105, 105, 105, 105, 105, 104, 104, 104, 104, 104, 104, 104, 104,
+    105, 105, 105, 105, 105, 105, 105, 105, 104, 104, 104, 104, 104, 104,   0,   0, 105, 105,
+    105, 105, 105, 105,   0,   0,   5, 104,   5, 104,   5, 104,   5, 104,   0, 105,   0, 105,
+      0, 105,   0, 105, 104, 104, 104, 104, 104, 104, 104, 104, 105, 105, 105, 105, 105, 105,
+    105, 105, 106, 106, 107, 107, 107, 107, 108, 108, 109, 109, 110, 110, 111, 111,   0,   0,
+    104, 104, 104, 104, 104, 104, 104, 104, 105, 105, 105, 105, 105, 105, 105, 105, 104, 104,
+    104, 104, 104, 104, 104, 104, 105, 105, 105, 105, 105, 105, 105, 105, 104, 104, 104, 104,
+    104, 104, 104, 104, 105, 105, 105, 105, 105, 105, 105, 105, 104, 104,   5, 112,   5,   0,
+      5,   5, 105, 105, 113, 113, 114,   0, 115,   0,   0,   0,   5, 112,   5,   0,   5,   5,
+    116, 116, 116, 116, 114,   0,   0,   0, 104, 104,   5,   5,   0,   0,   5,   5, 105, 105,
+    117, 117,   0,   0,   0,   0, 104, 104,   5,   5,   5,  88,   5,   5, 105, 105, 118, 118,
+     91,   0,   0,   0,   0,   0,   5, 112,   5,   0,   5,   5, 119, 119, 120, 120, 114,   0,
+      0,   0,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   0,   2,   2,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   1,   1,   0,   0,   0,   0,   0,   1,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   2,   2,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   2,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   1,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   2,   2,   2,   2,   2,   2,
+      2,   2,   2,   2,   2,   2,   2,  93,  93,  93,  93,   2,  93,  93,  93,   2,   2,   2,
+      2,   2,   2,   2,   2,   2,   2,   2,   2,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   5,   0,   0,   0,   0,   5,   0,   0,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   0,   5,   0,   0,   0,   5,   5,   5,   5,   5,
+      0,   0,   0,   0,   0,   0,   5,   0, 121,   0,   5,   0, 122, 123,   5,   5,   0,   5,
+      5,   5, 124,   5,   5,   5,   5,   5,   5,   5,   0,   0,   5,   5,   5,   5,   0,   0,
+      0,   0,   0,   5,   5,   5,   5,   5,   0,   0,   0,   0, 125,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 126, 126, 126, 126, 126, 126,
+    126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 127, 127, 127, 127, 127, 127, 127, 127,
+    127, 127, 127, 127, 127, 127, 127, 127,   5,   5,   5,   8,   9,   5,   5,   5,   5,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+    128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
+    128, 128, 128, 128, 128, 128, 128, 128, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129,
+    129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,
+     96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,
+     96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,   0,  97,  97,  97,  97,
+     97,  97,  97,  97,  97,  97,  97,  97,  97,  97,  97,  97,  97,  97,  97,  97,  97,  97,
+     97,  97,  97,  97,  97,  97,  97,  97,  97,  97,  97,  97,  97,  97,  97,  97,  97,  97,
+     97,  97,  97,  97,  97,  97,  97,   0,   8,   9, 130, 131, 132, 133, 134,   8,   9,   8,
+      9,   8,   9, 135, 136, 137, 138,   5,   8,   9,   5,   8,   9,   5,   5,   5,   5,   5,
+      5,   5, 139, 139,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,
+      8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,
+      8,   9,   8,   9,   5,   0,   0,   0,   0,   0,   0,   8,   9,   8,   9,   2,   2,   2,
+      8,   9,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 140, 140, 140, 140,
+    140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140,
+    140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140,   0, 140,
+      0,   0,   0,   0,   0, 140,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,   0,   0,   0,   0,   0,   5,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   2,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,
+      5,   0,   5,   5,   5,   5,   5,   5,   5,   0,   5,   5,   5,   5,   5,   5,   5,   0,
+      5,   5,   5,   5,   5,   5,   5,   0,   5,   5,   5,   5,   5,   5,   5,   0,   5,   5,
+      5,   5,   5,   5,   5,   0,   5,   5,   5,   5,   5,   5,   5,   0,   5,   5,   5,   5,
+      5,   5,   5,   0,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,
+      2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   1,   0,   0,   0,   0,   5,   5,   5,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   2,   2,
+      2,   2,  98,  98,   0,   5,   5,   5,   5,   5,   0,   0,   5,   5,   5,   5,   5,   0,
+      0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   0,   0,   2,   2,   0,   0,   5,   5,   5,   0,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
       5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
       5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,
-      0,   2,   2,   2,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      5,   5,   5,   5,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,   0,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
       0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
       5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,   0,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   2,   2,   2,   2,
+      2,   2,   2,   2,   2,   2,   5,   5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   8,   9,   8,   9,   8,   9,   8,   9,
+      8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,
+      8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,
+      8,   9,   5,   2,  93,  93,  93,   0,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,
+      0,   5,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,
+      8,   9,   8,   9,   8,   9,   8,   9,   0,   0,   0,   0,   0,   0,   0,   2,   5,   5,
       5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
       5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
       5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
       5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      5,   5,   5,   5,   5,   5,   2,   2,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      0,   0,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   5,   5,
+      8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,
+      8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,
+      8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,
+      8,   9,   8,   9,   8,   9,   8,   9,   5,   5,   5,   5,   5,   5,   5,   5,   5,   8,
+      9,   8,   9, 100,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   5,   0,   0,   8,
+      9,  56,   5,   0,   8,   9,   8,   9,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,  57,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   2,   5,   5,   5,   2,   5,   5,   5,
+      5,   2,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,  98,  98,   2,   2,  98,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,  98,  98,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,  98,  98,  98,  98,  98,  98,  98,  98,  98,  98,
+     98,  98,  98,  98,  98,  98,   2,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   0,   0,   0,   0,   0,   0,   2,   2,
+      2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   5,   5,
+      5,   5,   5,   5,   0,   0,   0,   5,   0,   0,   0,   0,   2,   2,   2,   2,   2,   2,
+      2,   2,   2,   2,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   2,   2,   2,   2,
+      2,   2,   2,   2,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   2,   2,   2,   2,   2,   2,   2,
+      2,   2,   2,   2,  98,  98,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,   0,   2,   2,   2,  98,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   2,  98,  98,   2,   2,   2,   2,
+     98,  98,   2,  98,  98,  98,  98,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   5,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   2,   2,   2,   2,   2,   2,  98,  98,   2,   2,  98,  98,   2,   2,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   5,   5,   5,   2,   5,   5,   5,   5,   5,   5,
+      5,   5,   2,  98,   0,   0,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   0,   0,
+      0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,   0,   5,  98,   0,   0,   0,   0,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   2,   5,   2,   2,   2,   5,
+      5,   2,   2,   5,   5,   5,   5,   5,   2,   2,   5,   2,   5,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   5,   5,   5,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,  98,
+      2,   2,  98,  98,   0,   0,   5,   5,   5,  98,   2,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   5,   5,   5,   5,   5,   5,   0,   0,   5,   5,   5,   5,   5,   5,   0,
+      0,   5,   5,   5,   5,   5,   5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,
+      5,   5,   5,   5,   5,   0,   5,   5,   5,   5,   5,   5,   5,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,  98,  98,   2,  98,  98,   2,  98,
+     98,   0,  98,   2,   0,   0,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   0,   0,
+      0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   0,   0,   0,   0,
+      0,   5,   2,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   5,   5,   5,   5,   5,   0,   5,   0,
+      5,   5,   0,   5,   5,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
       0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
       5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
       5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
       5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
       5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
       5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   1,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,   0,   0,   0,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   0,   0,   0,   5,   5,   5,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   0,   5,   5,   5,   5,   2,   2,   2,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   2,   2,   2,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   2,   2,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   0,   5,   5,   5,   0,   2,   2,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   0,   0,  97,   2,   2,   2,   2,   2,   2,   2,  97,  97,
-     97,  97,  97,  97,  97,  97,   2,  97,  97,   2,   2,   2,   2,   2,   2,   2,   2,   2,
-      2,   2,   0,   0,   0,   5,   0,   0,   0,   0,   5,   2,   0,   0,   2,   2,   2,   2,
-      2,   2,   2,   2,   2,   2,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   2,   2,   2,   1,   0,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,
-      0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   2,
-      5,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   0,   0,   0,   2,   2,   2,  97,  97,  97,  97,   2,
-      2,  97,  97,  97,   0,   0,   0,   0,  97,  97,   2,  97,  97,  97,  97,  97,  97,   2,
-      2,   2,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   2,   2,   2,   2,   2,   2,
-      2,   2,   2,   2,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,
-      5,   5,   5,   5,   5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   0,   0,   0,   0,  97,  97,  97,  97,  97,  97,  97,  97,
-     97,  97,  97,  97,  97,  97,  97,  97,  97,   5,   5,   5,   5,   5,   5,   5,  97,  97,
-      0,   0,   0,   0,   0,   0,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   2,   2,  97,  97,  97,   0,   0,   0,   0,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,  97,   2,  97,   2,   2,
-      2,   2,   2,   2,   2,   0,   2,  97,   2,  97,  97,   2,   2,   2,   2,   2,   2,   2,
-      2,  97,  97,  97,  97,  97,  97,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   0,
-      0,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   0,   0,   0,   0,   0,   0,
-      2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   2,   2,   2,   2,  97,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   2,  97,   2,   2,   2,   2,   2,  97,
-      2,  97,  97,  97,  97,  97,   2,  97,  97,   5,   5,   5,   5,   5,   5,   5,   0,   0,
-      0,   0,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   2,   2,   2,   2,   2,   2,   2,
-      2,   2,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   2,   2,  97,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,  97,   2,   2,   2,   2,  97,  97,
-      2,   2,  97,   0,   0,   0,   5,   5,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,
-      0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   2,  97,   2,   2,  97,  97,  97,   2,  97,   2,
-      2,   2,  97,  97,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,  97,  97,
-     97,  97,  97,  97,  97,  97,   2,   2,   2,   2,   2,   2,   2,   2,  97,  97,   2,   2,
-      0,   0,   0,   0,   0,   0,   0,   0,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,
-      0,   0,   0,   5,   5,   5,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   2,   2,
-      2,   0,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,  97,   2,   2,
-      2,   2,   2,   2,   2,   5,   5,   5,   5,   2,   5,   5,   5,   5,  97,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,  99,   5,   5,   5, 100,
-      5,   5,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,
-      2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,
-      2,   2,   2,   2,   2,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   2,   2,   2,   2,   8,   9,   8,   9,   8,   9,
-      8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,
-      8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,
-      8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,
-      8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,
-      8,   9,   8,   9,   8,   9,   8,   9,   5,   5,   5,   5,   5, 101,   5,   5, 102,   5,
-      8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,
-      8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9, 103, 103, 103, 103,
-    103, 103, 103, 103, 104, 104, 104, 104, 104, 104, 104, 104, 103, 103, 103, 103, 103, 103,
-      0,   0, 104, 104, 104, 104, 104, 104,   0,   0, 103, 103, 103, 103, 103, 103, 103, 103,
-    104, 104, 104, 104, 104, 104, 104, 104, 103, 103, 103, 103, 103, 103, 103, 103, 104, 104,
-    104, 104, 104, 104, 104, 104, 103, 103, 103, 103, 103, 103,   0,   0, 104, 104, 104, 104,
-    104, 104,   0,   0,   5, 103,   5, 103,   5, 103,   5, 103,   0, 104,   0, 104,   0, 104,
-      0, 104, 103, 103, 103, 103, 103, 103, 103, 103, 104, 104, 104, 104, 104, 104, 104, 104,
-    105, 105, 106, 106, 106, 106, 107, 107, 108, 108, 109, 109, 110, 110,   0,   0, 103, 103,
-    103, 103, 103, 103, 103, 103, 104, 104, 104, 104, 104, 104, 104, 104, 103, 103, 103, 103,
-    103, 103, 103, 103, 104, 104, 104, 104, 104, 104, 104, 104, 103, 103, 103, 103, 103, 103,
-    103, 103, 104, 104, 104, 104, 104, 104, 104, 104, 103, 103,   5, 111,   5,   0,   5,   5,
-    104, 104, 112, 112, 113,   0, 114,   0,   0,   0,   5, 111,   5,   0,   5,   5, 115, 115,
-    115, 115, 113,   0,   0,   0, 103, 103,   5,   5,   0,   0,   5,   5, 104, 104, 116, 116,
-      0,   0,   0,   0, 103, 103,   5,   5,   5,  87,   5,   5, 104, 104, 117, 117,  90,   0,
-      0,   0,   0,   0,   5, 111,   5,   0,   5,   5, 118, 118, 119, 119, 113,   0,   0,   0,
-      1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   0,   2,   2,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   1,   1,   0,   0,   0,   0,   0,   1,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   2,   2,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   2,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   1,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   2,   2,   2,   2,   2,   2,   2,   2,
-      2,   2,   2,   2,   2,  92,  92,  92,  92,   2,  92,  92,  92,   2,   2,   2,   2,   2,
-      2,   2,   2,   2,   2,   2,   2,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   5,   0,   0,   0,   0,   5,   0,   0,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   0,   5,   0,   0,   0,   5,   5,   5,   5,   5,   0,   0,
-      0,   0,   0,   0,   5,   0, 120,   0,   5,   0, 121, 122,   5,   5,   0,   5,   5,   5,
-    123,   5,   5,   5,   5,   5,   5,   5,   0,   0,   5,   5,   5,   5,   0,   0,   0,   0,
-      0,   5,   5,   5,   5,   5,   0,   0,   0,   0, 124,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 125, 125, 125, 125, 125, 125, 125, 125,
-    125, 125, 125, 125, 125, 125, 125, 125, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126,
-    126, 126, 126, 126, 126, 126,   5,   5,   5,   8,   9,   5,   5,   5,   5,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 127, 127,
-    127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
-    127, 127, 127, 127, 127, 127, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
-    128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-     95,  95,  95,  95,  95,  95,  95,  95,  95,  95,  95,  95,  95,  95,  95,  95,  95,  95,
-     95,  95,  95,  95,  95,  95,  95,  95,  95,  95,  95,  95,  95,  95,  95,  95,  95,  95,
-     95,  95,  95,  95,  95,  95,  95,  95,  95,  95,  95,   0,  96,  96,  96,  96,  96,  96,
-     96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,
-     96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,
-     96,  96,  96,  96,  96,   0,   8,   9, 129, 130, 131, 132, 133,   8,   9,   8,   9,   8,
-      9, 134, 135, 136, 137,   5,   8,   9,   5,   8,   9,   5,   5,   5,   5,   5,   5,   5,
-    138, 138,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,
-      8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,
-      8,   9,   5,   0,   0,   0,   0,   0,   0,   8,   9,   8,   9,   2,   2,   2,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 139, 139, 139, 139, 139, 139,
-    139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139,
-    139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   2,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   0,
-      5,   5,   5,   5,   5,   5,   5,   0,   5,   5,   5,   5,   5,   5,   5,   0,   5,   5,
-      5,   5,   5,   5,   5,   0,   5,   5,   5,   5,   5,   5,   5,   0,   5,   5,   5,   5,
-      5,   5,   5,   0,   5,   5,   5,   5,   5,   5,   5,   0,   5,   5,   5,   5,   5,   5,
-      5,   0,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,
-      2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   1,   0,   0,   0,   0,   5,   5,   5,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   2,   2,   2,   2,
-      2,   2,   0,   5,   5,   5,   5,   5,   0,   0,   5,   5,   5,   5,   5,   0,   0,   0,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   0,   0,   2,   2,   0,   0,   5,   5,   5,   0,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   5,   5,
-      5,   5,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,   0,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   5,   5,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,
-      8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,
-      8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   5,   2,  92,  92,  92,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   2,   2,   0,   5,   8,   9,   8,   9,   8,   9,   8,   9,
-      8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   0,   0,
-      0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   2,   2,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   0,   0,   8,   9,   8,   9,   8,   9,   8,   9,
-      8,   9,   8,   9,   8,   9,   5,   5,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,
-      8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,
-      8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,
-      8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   8,   9,   8,   9,  99,   8,   9,   8,   9,   8,   9,
-      8,   9,   8,   9,   5,   0,   0,   8,   9,  56,   5,   0,   8,   9,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   8,   9,   8,   9,   8,   9,   8,   9,
-      8,   9,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,
-      2,   5,   5,   5,   2,   5,   5,   5,   5,   2,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,  97,  97,   2,
-      2,  97,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-     97,  97,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,  97,  97,
-     97,  97,  97,  97,  97,  97,  97,  97,  97,  97,  97,  97,  97,  97,   2,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,
-      0,   0,   0,   0,   0,   0,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,
-      2,   2,   2,   2,   2,   2,   5,   5,   5,   5,   5,   5,   0,   0,   0,   5,   0,   0,
-      0,   0,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   2,   2,   2,   2,   2,   2,   2,   2,   0,   0,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,  97,  97,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   0,   0,   0,   2,   2,   2,  97,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   2,  97,  97,   2,   2,   2,   2,  97,  97,   2,  97,  97,  97,  97,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   2,   2,   2,   2,   2,   2,
-      2,   2,   2,   2,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   2,   2,   2,   2,   2,   2,  97,
-     97,   2,   2,  97,  97,   2,   2,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,
-      5,   2,   5,   5,   5,   5,   5,   5,   5,   5,   2,  97,   0,   0,   2,   2,   2,   2,
-      2,   2,   2,   2,   2,   2,   0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,
-      0,   0,   5,  97,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   2,   5,   2,   2,   2,   5,   5,   2,   2,   5,   5,   5,   5,   5,   2,   2,
-      5,   2,   5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,   5,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   0,
-      0,   5,   5,   5,   5,   5,   5,   0,   0,   5,   5,   5,   5,   5,   5,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   0,   5,   5,   5,   5,
-      5,   5,   5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      5,   5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
       0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
       5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,  97,  97,   2,  97,  97,   2,  97,  97,   0,  97,   2,   0,   0,   2,   2,   2,   2,
-      2,   2,   2,   2,   2,   2,   0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,   0,   0,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,   0,   0,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,
-      5,   5,   5,   5,   5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,
-      5,   5,   5,   5,   0,   0,   0,   0,   0,   5,   2,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,
-      5,   5,   5,   5,   5,   0,   5,   0,   5,   5,   0,   5,   5,   0,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
       5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,   5,   5,   5,   5,
       5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
       5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
       5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,   0,   0,
       0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
       0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   0,   0,   0,   0,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,
-      2,   2,   2,   2,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   2,   2,   2,   2,   2,   2,   2,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   2,   2,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   2,   2,   2,   0,   0,   0,   0,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,   0,   0,   2,   2,
+      2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   2,   2,   2,   2,   2,   2,
+      2,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   2,   2,   0,   0,   0,
       0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   0,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,   1,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   2,   2,
-      2,   2,   2,   2,   2,   2,   2,   2,   0,   0,   0,   0,   0,   0,   0,   3,   3,   3,
-      3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,
-      3,   3,   3,   3,   3,   0,   0,   0,   0,   2,   0,   4,   4,   4,   4,   4,   4,   4,
-      4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,
-      4,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,   0,   5,   5,   5,   5,
-      5,   5,   0,   0,   5,   5,   5,   5,   5,   5,   0,   0,   5,   5,   5,   5,   5,   5,
-      0,   0,   5,   5,   5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   2,   2,   2,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
       0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,
+      0,   0,   5,   5,   5,   5,   5,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   0,   0,   1,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,
+      0,   0,   0,   0,   0,   0,   0,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,
+      3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   0,   0,   0,
+      0,   2,   0,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,
+      4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   0,   0,   0,   5,   5,   5,   5,   5,   5,   0,   0,   5,   5,   5,   5,
+      5,   5,   0,   0,   5,   5,   5,   5,   5,   5,   0,   0,   5,   5,   5,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
 };
 
 } /* namespace unicode */
diff --git a/js/src/vm/UnicodeData.txt b/js/src/vm/UnicodeData.txt
index 8d7222b13789..9f204050c6bb 100644
--- a/js/src/vm/UnicodeData.txt
+++ b/js/src/vm/UnicodeData.txt
@@ -165,10 +165,10 @@
 00A4;CURRENCY SIGN;Sc;0;ET;;;;;N;;;;;
 00A5;YEN SIGN;Sc;0;ET;;;;;N;;;;;
 00A6;BROKEN BAR;So;0;ON;;;;;N;BROKEN VERTICAL BAR;;;;
-00A7;SECTION SIGN;So;0;ON;;;;;N;;;;;
+00A7;SECTION SIGN;Po;0;ON;;;;;N;;;;;
 00A8;DIAERESIS;Sk;0;ON;<compat> 0020 0308;;;;N;SPACING DIAERESIS;;;;
 00A9;COPYRIGHT SIGN;So;0;ON;;;;;N;;;;;
-00AA;FEMININE ORDINAL INDICATOR;Ll;0;L;<super> 0061;;;;N;;;;;
+00AA;FEMININE ORDINAL INDICATOR;Lo;0;L;<super> 0061;;;;N;;;;;
 00AB;LEFT-POINTING DOUBLE ANGLE QUOTATION MARK;Pi;0;ON;;;;;Y;LEFT POINTING GUILLEMET;;;;
 00AC;NOT SIGN;Sm;0;ON;;;;;N;;;;;
 00AD;SOFT HYPHEN;Cf;0;BN;;;;;N;;;;;
@@ -180,11 +180,11 @@
 00B3;SUPERSCRIPT THREE;No;0;EN;<super> 0033;;3;3;N;SUPERSCRIPT DIGIT THREE;;;;
 00B4;ACUTE ACCENT;Sk;0;ON;<compat> 0020 0301;;;;N;SPACING ACUTE;;;;
 00B5;MICRO SIGN;Ll;0;L;<compat> 03BC;;;;N;;;039C;;039C
-00B6;PILCROW SIGN;So;0;ON;;;;;N;PARAGRAPH SIGN;;;;
+00B6;PILCROW SIGN;Po;0;ON;;;;;N;PARAGRAPH SIGN;;;;
 00B7;MIDDLE DOT;Po;0;ON;;;;;N;;;;;
 00B8;CEDILLA;Sk;0;ON;<compat> 0020 0327;;;;N;SPACING CEDILLA;;;;
 00B9;SUPERSCRIPT ONE;No;0;EN;<super> 0031;;1;1;N;SUPERSCRIPT DIGIT ONE;;;;
-00BA;MASCULINE ORDINAL INDICATOR;Ll;0;L;<super> 006F;;;;N;;;;;
+00BA;MASCULINE ORDINAL INDICATOR;Lo;0;L;<super> 006F;;;;N;;;;;
 00BB;RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK;Pf;0;ON;;;;;Y;RIGHT POINTING GUILLEMET;;;;
 00BC;VULGAR FRACTION ONE QUARTER;No;0;ON;<fraction> 0031 2044 0034;;;1/4;N;FRACTION ONE QUARTER;;;;
 00BD;VULGAR FRACTION ONE HALF;No;0;ON;<fraction> 0031 2044 0032;;;1/2;N;FRACTION ONE HALF;;;;
@@ -612,7 +612,7 @@
 0263;LATIN SMALL LETTER GAMMA;Ll;0;L;;;;;N;;;0194;;0194
 0264;LATIN SMALL LETTER RAMS HORN;Ll;0;L;;;;;N;LATIN SMALL LETTER BABY GAMMA;;;;
 0265;LATIN SMALL LETTER TURNED H;Ll;0;L;;;;;N;;;A78D;;A78D
-0266;LATIN SMALL LETTER H WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER H HOOK;;;;
+0266;LATIN SMALL LETTER H WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER H HOOK;;A7AA;;A7AA
 0267;LATIN SMALL LETTER HENG WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER HENG HOOK;;;;
 0268;LATIN SMALL LETTER I WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER BARRED I;;0197;;0197
 0269;LATIN SMALL LETTER IOTA;Ll;0;L;;;;;N;;;0196;;0196
@@ -1394,6 +1394,7 @@
 0587;ARMENIAN SMALL LIGATURE ECH YIWN;Ll;0;L;<compat> 0565 0582;;;;N;;;;;
 0589;ARMENIAN FULL STOP;Po;0;L;;;;;N;ARMENIAN PERIOD;;;;
 058A;ARMENIAN HYPHEN;Pd;0;ON;;;;;N;;;;;
+058F;ARMENIAN DRAM SIGN;Sc;0;ET;;;;;N;;;;;
 0591;HEBREW ACCENT ETNAHTA;Mn;220;NSM;;;;;N;;;;;
 0592;HEBREW ACCENT SEGOL;Mn;230;NSM;;;;;N;;;;;
 0593;HEBREW ACCENT SHALSHELET;Mn;230;NSM;;;;;N;;;;;
@@ -1485,6 +1486,7 @@
 0601;ARABIC SIGN SANAH;Cf;0;AN;;;;;N;;;;;
 0602;ARABIC FOOTNOTE MARKER;Cf;0;AN;;;;;N;;;;;
 0603;ARABIC SIGN SAFHA;Cf;0;AN;;;;;N;;;;;
+0604;ARABIC SIGN SAMVAT;Cf;0;AN;;;;;N;;;;;
 0606;ARABIC-INDIC CUBE ROOT;Sm;0;ON;;;;;N;;;;;
 0607;ARABIC-INDIC FOURTH ROOT;Sm;0;ON;;;;;N;;;;;
 0608;ARABIC RAY;Sm;0;AL;;;;;N;;;;;
@@ -1747,7 +1749,7 @@
 070B;SYRIAC HARKLEAN OBELUS;Po;0;AL;;;;;N;;;;;
 070C;SYRIAC HARKLEAN METOBELUS;Po;0;AL;;;;;N;;;;;
 070D;SYRIAC HARKLEAN ASTERISCUS;Po;0;AL;;;;;N;;;;;
-070F;SYRIAC ABBREVIATION MARK;Cf;0;AN;;;;;N;;;;;
+070F;SYRIAC ABBREVIATION MARK;Cf;0;AL;;;;;N;;;;;
 0710;SYRIAC LETTER ALAPH;Lo;0;AL;;;;;N;;;;;
 0711;SYRIAC LETTER SUPERSCRIPT ALAPH;Mn;36;NSM;;;;;N;;;;;
 0712;SYRIAC LETTER BETH;Lo;0;AL;;;;;N;;;;;
@@ -2057,6 +2059,45 @@
 085A;MANDAIC VOCALIZATION MARK;Mn;220;NSM;;;;;N;;;;;
 085B;MANDAIC GEMINATION MARK;Mn;220;NSM;;;;;N;;;;;
 085E;MANDAIC PUNCTUATION;Po;0;R;;;;;N;;;;;
+08A0;ARABIC LETTER BEH WITH SMALL V BELOW;Lo;0;AL;;;;;N;;;;;
+08A2;ARABIC LETTER JEEM WITH TWO DOTS ABOVE;Lo;0;AL;;;;;N;;;;;
+08A3;ARABIC LETTER TAH WITH TWO DOTS ABOVE;Lo;0;AL;;;;;N;;;;;
+08A4;ARABIC LETTER FEH WITH DOT BELOW AND THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;;
+08A5;ARABIC LETTER QAF WITH DOT BELOW;Lo;0;AL;;;;;N;;;;;
+08A6;ARABIC LETTER LAM WITH DOUBLE BAR;Lo;0;AL;;;;;N;;;;;
+08A7;ARABIC LETTER MEEM WITH THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;;
+08A8;ARABIC LETTER YEH WITH TWO DOTS BELOW AND HAMZA ABOVE;Lo;0;AL;;;;;N;;;;;
+08A9;ARABIC LETTER YEH WITH TWO DOTS BELOW AND DOT ABOVE;Lo;0;AL;;;;;N;;;;;
+08AA;ARABIC LETTER REH WITH LOOP;Lo;0;AL;;;;;N;;;;;
+08AB;ARABIC LETTER WAW WITH DOT WITHIN;Lo;0;AL;;;;;N;;;;;
+08AC;ARABIC LETTER ROHINGYA YEH;Lo;0;AL;;;;;N;;;;;
+08E4;ARABIC CURLY FATHA;Mn;230;NSM;;;;;N;;;;;
+08E5;ARABIC CURLY DAMMA;Mn;230;NSM;;;;;N;;;;;
+08E6;ARABIC CURLY KASRA;Mn;220;NSM;;;;;N;;;;;
+08E7;ARABIC CURLY FATHATAN;Mn;230;NSM;;;;;N;;;;;
+08E8;ARABIC CURLY DAMMATAN;Mn;230;NSM;;;;;N;;;;;
+08E9;ARABIC CURLY KASRATAN;Mn;220;NSM;;;;;N;;;;;
+08EA;ARABIC TONE ONE DOT ABOVE;Mn;230;NSM;;;;;N;;;;;
+08EB;ARABIC TONE TWO DOTS ABOVE;Mn;230;NSM;;;;;N;;;;;
+08EC;ARABIC TONE LOOP ABOVE;Mn;230;NSM;;;;;N;;;;;
+08ED;ARABIC TONE ONE DOT BELOW;Mn;220;NSM;;;;;N;;;;;
+08EE;ARABIC TONE TWO DOTS BELOW;Mn;220;NSM;;;;;N;;;;;
+08EF;ARABIC TONE LOOP BELOW;Mn;220;NSM;;;;;N;;;;;
+08F0;ARABIC OPEN FATHATAN;Mn;27;NSM;;;;;N;;;;;
+08F1;ARABIC OPEN DAMMATAN;Mn;28;NSM;;;;;N;;;;;
+08F2;ARABIC OPEN KASRATAN;Mn;29;NSM;;;;;N;;;;;
+08F3;ARABIC SMALL HIGH WAW;Mn;230;NSM;;;;;N;;;;;
+08F4;ARABIC FATHA WITH RING;Mn;230;NSM;;;;;N;;;;;
+08F5;ARABIC FATHA WITH DOT ABOVE;Mn;230;NSM;;;;;N;;;;;
+08F6;ARABIC KASRA WITH DOT BELOW;Mn;220;NSM;;;;;N;;;;;
+08F7;ARABIC LEFT ARROWHEAD ABOVE;Mn;230;NSM;;;;;N;;;;;
+08F8;ARABIC RIGHT ARROWHEAD ABOVE;Mn;230;NSM;;;;;N;;;;;
+08F9;ARABIC LEFT ARROWHEAD BELOW;Mn;220;NSM;;;;;N;;;;;
+08FA;ARABIC RIGHT ARROWHEAD BELOW;Mn;220;NSM;;;;;N;;;;;
+08FB;ARABIC DOUBLE RIGHT ARROWHEAD ABOVE;Mn;230;NSM;;;;;N;;;;;
+08FC;ARABIC DOUBLE RIGHT ARROWHEAD ABOVE WITH DOT;Mn;230;NSM;;;;;N;;;;;
+08FD;ARABIC RIGHT ARROWHEAD ABOVE WITH DOT;Mn;230;NSM;;;;;N;;;;;
+08FE;ARABIC DAMMA WITH DOT;Mn;230;NSM;;;;;N;;;;;
 0900;DEVANAGARI SIGN INVERTED CANDRABINDU;Mn;0;NSM;;;;;N;;;;;
 0901;DEVANAGARI SIGN CANDRABINDU;Mn;0;NSM;;;;;N;;;;;
 0902;DEVANAGARI SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;;
@@ -2437,6 +2478,7 @@
 0AED;GUJARATI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
 0AEE;GUJARATI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
 0AEF;GUJARATI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+0AF0;GUJARATI ABBREVIATION SIGN;Po;0;L;;;;;N;;;;;
 0AF1;GUJARATI RUPEE SIGN;Sc;0;ET;;;;;N;;;;;
 0B01;ORIYA SIGN CANDRABINDU;Mn;0;NSM;;;;;N;;;;;
 0B02;ORIYA SIGN ANUSVARA;Mc;0;L;;;;;N;;;;;
@@ -3109,6 +3151,8 @@
 0ED9;LAO DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
 0EDC;LAO HO NO;Lo;0;L;<compat> 0EAB 0E99;;;;N;;;;;
 0EDD;LAO HO MO;Lo;0;L;<compat> 0EAB 0EA1;;;;N;;;;;
+0EDE;LAO LETTER KHMU GO;Lo;0;L;;;;;N;;;;;
+0EDF;LAO LETTER KHMU NYO;Lo;0;L;;;;;N;;;;;
 0F00;TIBETAN SYLLABLE OM;Lo;0;L;;;;;N;;;;;
 0F01;TIBETAN MARK GTER YIG MGO TRUNCATED A;So;0;L;;;;;N;;;;;
 0F02;TIBETAN MARK GTER YIG MGO -UM RNAM BCAD MA;So;0;L;;;;;N;;;;;
@@ -3129,7 +3173,7 @@
 0F11;TIBETAN MARK RIN CHEN SPUNGS SHAD;Po;0;L;;;;;N;TIBETAN RINCHANPHUNGSHAD;;;;
 0F12;TIBETAN MARK RGYA GRAM SHAD;Po;0;L;;;;;N;;;;;
 0F13;TIBETAN MARK CARET -DZUD RTAGS ME LONG CAN;So;0;L;;;;;N;;;;;
-0F14;TIBETAN MARK GTER TSHEG;So;0;L;;;;;N;TIBETAN COMMA;;;;
+0F14;TIBETAN MARK GTER TSHEG;Po;0;L;;;;;N;TIBETAN COMMA;;;;
 0F15;TIBETAN LOGOTYPE SIGN CHAD RTAGS;So;0;L;;;;;N;;;;;
 0F16;TIBETAN LOGOTYPE SIGN LHAG RTAGS;So;0;L;;;;;N;;;;;
 0F17;TIBETAN ASTROLOGICAL SIGN SGRA GCAN -CHAR RTAGS;So;0;L;;;;;N;;;;;
@@ -3518,6 +3562,8 @@
 10C3;GEORGIAN CAPITAL LETTER WE;Lu;0;L;;;;;N;;;;2D23;
 10C4;GEORGIAN CAPITAL LETTER HAR;Lu;0;L;;;;;N;;;;2D24;
 10C5;GEORGIAN CAPITAL LETTER HOE;Lu;0;L;;;;;N;;;;2D25;
+10C7;GEORGIAN CAPITAL LETTER YN;Lu;0;L;;;;;N;;;;2D27;
+10CD;GEORGIAN CAPITAL LETTER AEN;Lu;0;L;;;;;N;;;;2D2D;
 10D0;GEORGIAN LETTER AN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER AN;;;;
 10D1;GEORGIAN LETTER BAN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER BAN;;;;
 10D2;GEORGIAN LETTER GAN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER GAN;;;;
@@ -3563,6 +3609,9 @@
 10FA;GEORGIAN LETTER AIN;Lo;0;L;;;;;N;;;;;
 10FB;GEORGIAN PARAGRAPH SEPARATOR;Po;0;L;;;;;N;;;;;
 10FC;MODIFIER LETTER GEORGIAN NAR;Lm;0;L;<super> 10DC;;;;N;;;;;
+10FD;GEORGIAN LETTER AEN;Lo;0;L;;;;;N;;;;;
+10FE;GEORGIAN LETTER HARD SIGN;Lo;0;L;;;;;N;;;;;
+10FF;GEORGIAN LETTER LABIAL SIGN;Lo;0;L;;;;;N;;;;;
 1100;HANGUL CHOSEONG KIYEOK;Lo;0;L;;;;;N;;;;;
 1101;HANGUL CHOSEONG SSANGKIYEOK;Lo;0;L;;;;;N;;;;;
 1102;HANGUL CHOSEONG NIEUN;Lo;0;L;;;;;N;;;;;
@@ -4148,7 +4197,7 @@
 135D;ETHIOPIC COMBINING GEMINATION AND VOWEL LENGTH MARK;Mn;230;NSM;;;;;N;;;;;
 135E;ETHIOPIC COMBINING VOWEL LENGTH MARK;Mn;230;NSM;;;;;N;;;;;
 135F;ETHIOPIC COMBINING GEMINATION MARK;Mn;230;NSM;;;;;N;;;;;
-1360;ETHIOPIC SECTION MARK;So;0;L;;;;;N;;;;;
+1360;ETHIOPIC SECTION MARK;Po;0;L;;;;;N;;;;;
 1361;ETHIOPIC WORDSPACE;Po;0;L;;;;;N;;;;;
 1362;ETHIOPIC FULL STOP;Po;0;L;;;;;N;;;;;
 1363;ETHIOPIC COMMA;Po;0;L;;;;;N;;;;;
@@ -5171,8 +5220,8 @@
 17B1;KHMER INDEPENDENT VOWEL QOO TYPE ONE;Lo;0;L;;;;;N;;;;;
 17B2;KHMER INDEPENDENT VOWEL QOO TYPE TWO;Lo;0;L;;;;;N;;;;;
 17B3;KHMER INDEPENDENT VOWEL QAU;Lo;0;L;;;;;N;;;;;
-17B4;KHMER VOWEL INHERENT AQ;Cf;0;L;;;;;N;;;;;
-17B5;KHMER VOWEL INHERENT AA;Cf;0;L;;;;;N;;;;;
+17B4;KHMER VOWEL INHERENT AQ;Mn;0;NSM;;;;;N;;;;;
+17B5;KHMER VOWEL INHERENT AA;Mn;0;NSM;;;;;N;;;;;
 17B6;KHMER VOWEL SIGN AA;Mc;0;L;;;;;N;;;;;
 17B7;KHMER VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;;
 17B8;KHMER VOWEL SIGN II;Mn;0;NSM;;;;;N;;;;;
@@ -5996,6 +6045,9 @@
 1BA8;SUNDANESE VOWEL SIGN PAMEPET;Mn;0;NSM;;;;;N;;;;;
 1BA9;SUNDANESE VOWEL SIGN PANEULEUNG;Mn;0;NSM;;;;;N;;;;;
 1BAA;SUNDANESE SIGN PAMAAEH;Mc;9;L;;;;;N;;;;;
+1BAB;SUNDANESE SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;;
+1BAC;SUNDANESE CONSONANT SIGN PASANGAN MA;Mc;0;L;;;;;N;;;;;
+1BAD;SUNDANESE CONSONANT SIGN PASANGAN WA;Mc;0;L;;;;;N;;;;;
 1BAE;SUNDANESE LETTER KHA;Lo;0;L;;;;;N;;;;;
 1BAF;SUNDANESE LETTER SYA;Lo;0;L;;;;;N;;;;;
 1BB0;SUNDANESE DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
@@ -6008,6 +6060,12 @@
 1BB7;SUNDANESE DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
 1BB8;SUNDANESE DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
 1BB9;SUNDANESE DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+1BBA;SUNDANESE AVAGRAHA;Lo;0;L;;;;;N;;;;;
+1BBB;SUNDANESE LETTER REU;Lo;0;L;;;;;N;;;;;
+1BBC;SUNDANESE LETTER LEU;Lo;0;L;;;;;N;;;;;
+1BBD;SUNDANESE LETTER BHA;Lo;0;L;;;;;N;;;;;
+1BBE;SUNDANESE LETTER FINAL K;Lo;0;L;;;;;N;;;;;
+1BBF;SUNDANESE LETTER FINAL M;Lo;0;L;;;;;N;;;;;
 1BC0;BATAK LETTER A;Lo;0;L;;;;;N;;;;;
 1BC1;BATAK LETTER SIMALUNGUN A;Lo;0;L;;;;;N;;;;;
 1BC2;BATAK LETTER HA;Lo;0;L;;;;;N;;;;;
@@ -6186,6 +6244,14 @@
 1C7D;OL CHIKI AHAD;Lm;0;L;;;;;N;;;;;
 1C7E;OL CHIKI PUNCTUATION MUCAAD;Po;0;L;;;;;N;;;;;
 1C7F;OL CHIKI PUNCTUATION DOUBLE MUCAAD;Po;0;L;;;;;N;;;;;
+1CC0;SUNDANESE PUNCTUATION BINDU SURYA;Po;0;L;;;;;N;;;;;
+1CC1;SUNDANESE PUNCTUATION BINDU PANGLONG;Po;0;L;;;;;N;;;;;
+1CC2;SUNDANESE PUNCTUATION BINDU PURNAMA;Po;0;L;;;;;N;;;;;
+1CC3;SUNDANESE PUNCTUATION BINDU CAKRA;Po;0;L;;;;;N;;;;;
+1CC4;SUNDANESE PUNCTUATION BINDU LEU SATANGA;Po;0;L;;;;;N;;;;;
+1CC5;SUNDANESE PUNCTUATION BINDU KA SATANGA;Po;0;L;;;;;N;;;;;
+1CC6;SUNDANESE PUNCTUATION BINDU DA SATANGA;Po;0;L;;;;;N;;;;;
+1CC7;SUNDANESE PUNCTUATION BINDU BA SATANGA;Po;0;L;;;;;N;;;;;
 1CD0;VEDIC TONE KARSHANA;Mn;230;NSM;;;;;N;;;;;
 1CD1;VEDIC TONE SHARA;Mn;230;NSM;;;;;N;;;;;
 1CD2;VEDIC TONE PRENKHA;Mn;230;NSM;;;;;N;;;;;
@@ -6221,6 +6287,10 @@
 1CF0;VEDIC SIGN RTHANG LONG ANUSVARA;Lo;0;L;;;;;N;;;;;
 1CF1;VEDIC SIGN ANUSVARA UBHAYATO MUKHA;Lo;0;L;;;;;N;;;;;
 1CF2;VEDIC SIGN ARDHAVISARGA;Mc;0;L;;;;;N;;;;;
+1CF3;VEDIC SIGN ROTATED ARDHAVISARGA;Mc;0;L;;;;;N;;;;;
+1CF4;VEDIC TONE CANDRA ABOVE;Mn;230;NSM;;;;;N;;;;;
+1CF5;VEDIC SIGN JIHVAMULIYA;Lo;0;L;;;;;N;;;;;
+1CF6;VEDIC SIGN UPADHMANIYA;Lo;0;L;;;;;N;;;;;
 1D00;LATIN LETTER SMALL CAPITAL A;Ll;0;L;;;;;N;;;;;
 1D01;LATIN LETTER SMALL CAPITAL AE;Ll;0;L;;;;;N;;;;;
 1D02;LATIN SMALL LETTER TURNED AE;Ll;0;L;;;;;N;;;;;
@@ -6319,15 +6389,15 @@
 1D5F;MODIFIER LETTER SMALL DELTA;Lm;0;L;<super> 03B4;;;;N;;;;;
 1D60;MODIFIER LETTER SMALL GREEK PHI;Lm;0;L;<super> 03C6;;;;N;;;;;
 1D61;MODIFIER LETTER SMALL CHI;Lm;0;L;<super> 03C7;;;;N;;;;;
-1D62;LATIN SUBSCRIPT SMALL LETTER I;Ll;0;L;<sub> 0069;;;;N;;;;;
-1D63;LATIN SUBSCRIPT SMALL LETTER R;Ll;0;L;<sub> 0072;;;;N;;;;;
-1D64;LATIN SUBSCRIPT SMALL LETTER U;Ll;0;L;<sub> 0075;;;;N;;;;;
-1D65;LATIN SUBSCRIPT SMALL LETTER V;Ll;0;L;<sub> 0076;;;;N;;;;;
-1D66;GREEK SUBSCRIPT SMALL LETTER BETA;Ll;0;L;<sub> 03B2;;;;N;;;;;
-1D67;GREEK SUBSCRIPT SMALL LETTER GAMMA;Ll;0;L;<sub> 03B3;;;;N;;;;;
-1D68;GREEK SUBSCRIPT SMALL LETTER RHO;Ll;0;L;<sub> 03C1;;;;N;;;;;
-1D69;GREEK SUBSCRIPT SMALL LETTER PHI;Ll;0;L;<sub> 03C6;;;;N;;;;;
-1D6A;GREEK SUBSCRIPT SMALL LETTER CHI;Ll;0;L;<sub> 03C7;;;;N;;;;;
+1D62;LATIN SUBSCRIPT SMALL LETTER I;Lm;0;L;<sub> 0069;;;;N;;;;;
+1D63;LATIN SUBSCRIPT SMALL LETTER R;Lm;0;L;<sub> 0072;;;;N;;;;;
+1D64;LATIN SUBSCRIPT SMALL LETTER U;Lm;0;L;<sub> 0075;;;;N;;;;;
+1D65;LATIN SUBSCRIPT SMALL LETTER V;Lm;0;L;<sub> 0076;;;;N;;;;;
+1D66;GREEK SUBSCRIPT SMALL LETTER BETA;Lm;0;L;<sub> 03B2;;;;N;;;;;
+1D67;GREEK SUBSCRIPT SMALL LETTER GAMMA;Lm;0;L;<sub> 03B3;;;;N;;;;;
+1D68;GREEK SUBSCRIPT SMALL LETTER RHO;Lm;0;L;<sub> 03C1;;;;N;;;;;
+1D69;GREEK SUBSCRIPT SMALL LETTER PHI;Lm;0;L;<sub> 03C6;;;;N;;;;;
+1D6A;GREEK SUBSCRIPT SMALL LETTER CHI;Lm;0;L;<sub> 03C7;;;;N;;;;;
 1D6B;LATIN SMALL LETTER UE;Ll;0;L;;;;;N;;;;;
 1D6C;LATIN SMALL LETTER B WITH MIDDLE TILDE;Ll;0;L;;;;;N;;;;;
 1D6D;LATIN SMALL LETTER D WITH MIDDLE TILDE;Ll;0;L;;;;;N;;;;;
@@ -8827,7 +8897,9 @@
 27C8;REVERSE SOLIDUS PRECEDING SUBSET;Sm;0;ON;;;;;Y;;;;;
 27C9;SUPERSET PRECEDING SOLIDUS;Sm;0;ON;;;;;Y;;;;;
 27CA;VERTICAL BAR WITH HORIZONTAL STROKE;Sm;0;ON;;;;;N;;;;;
+27CB;MATHEMATICAL RISING DIAGONAL;Sm;0;ON;;;;;Y;;;;;
 27CC;LONG DIVISION;Sm;0;ON;;;;;Y;;;;;
+27CD;MATHEMATICAL FALLING DIAGONAL;Sm;0;ON;;;;;Y;;;;;
 27CE;SQUARED LOGICAL AND;Sm;0;ON;;;;;N;;;;;
 27CF;SQUARED LOGICAL OR;Sm;0;ON;;;;;N;;;;;
 27D0;WHITE DIAMOND WITH CENTRED DOT;Sm;0;ON;;;;;N;;;;;
@@ -9855,7 +9927,7 @@
 2C79;LATIN SMALL LETTER TURNED R WITH TAIL;Ll;0;L;;;;;N;;;;;
 2C7A;LATIN SMALL LETTER O WITH LOW RING INSIDE;Ll;0;L;;;;;N;;;;;
 2C7B;LATIN LETTER SMALL CAPITAL TURNED E;Ll;0;L;;;;;N;;;;;
-2C7C;LATIN SUBSCRIPT SMALL LETTER J;Ll;0;L;<sub> 006A;;;;N;;;;;
+2C7C;LATIN SUBSCRIPT SMALL LETTER J;Lm;0;L;<sub> 006A;;;;N;;;;;
 2C7D;MODIFIER LETTER CAPITAL V;Lm;0;L;<super> 0056;;;;N;;;;;
 2C7E;LATIN CAPITAL LETTER S WITH SWASH TAIL;Lu;0;L;;;;;N;;;;023F;
 2C7F;LATIN CAPITAL LETTER Z WITH SWASH TAIL;Lu;0;L;;;;;N;;;;0240;
@@ -9973,6 +10045,8 @@
 2CEF;COPTIC COMBINING NI ABOVE;Mn;230;NSM;;;;;N;;;;;
 2CF0;COPTIC COMBINING SPIRITUS ASPER;Mn;230;NSM;;;;;N;;;;;
 2CF1;COPTIC COMBINING SPIRITUS LENIS;Mn;230;NSM;;;;;N;;;;;
+2CF2;COPTIC CAPITAL LETTER BOHAIRIC KHEI;Lu;0;L;;;;;N;;;;2CF3;
+2CF3;COPTIC SMALL LETTER BOHAIRIC KHEI;Ll;0;L;;;;;N;;;2CF2;;2CF2
 2CF9;COPTIC OLD NUBIAN FULL STOP;Po;0;ON;;;;;N;;;;;
 2CFA;COPTIC OLD NUBIAN DIRECT QUESTION MARK;Po;0;ON;;;;;N;;;;;
 2CFB;COPTIC OLD NUBIAN INDIRECT QUESTION MARK;Po;0;ON;;;;;N;;;;;
@@ -10018,6 +10092,8 @@
 2D23;GEORGIAN SMALL LETTER WE;Ll;0;L;;;;;N;;;10C3;;10C3
 2D24;GEORGIAN SMALL LETTER HAR;Ll;0;L;;;;;N;;;10C4;;10C4
 2D25;GEORGIAN SMALL LETTER HOE;Ll;0;L;;;;;N;;;10C5;;10C5
+2D27;GEORGIAN SMALL LETTER YN;Ll;0;L;;;;;N;;;10C7;;10C7
+2D2D;GEORGIAN SMALL LETTER AEN;Ll;0;L;;;;;N;;;10CD;;10CD
 2D30;TIFINAGH LETTER YA;Lo;0;L;;;;;N;;;;;
 2D31;TIFINAGH LETTER YAB;Lo;0;L;;;;;N;;;;;
 2D32;TIFINAGH LETTER YABH;Lo;0;L;;;;;N;;;;;
@@ -10072,6 +10148,8 @@
 2D63;TIFINAGH LETTER YAZ;Lo;0;L;;;;;N;;;;;
 2D64;TIFINAGH LETTER TAWELLEMET YAZ;Lo;0;L;;;;;N;;;;;
 2D65;TIFINAGH LETTER YAZZ;Lo;0;L;;;;;N;;;;;
+2D66;TIFINAGH LETTER YE;Lo;0;L;;;;;N;;;;;
+2D67;TIFINAGH LETTER YO;Lo;0;L;;;;;N;;;;;
 2D6F;TIFINAGH MODIFIER LETTER LABIALIZATION MARK;Lm;0;L;<super> 2D61;;;;N;;;;;
 2D70;TIFINAGH SEPARATOR MARK;Po;0;L;;;;;N;;;;;
 2D7F;TIFINAGH CONSONANT JOINER;Mn;9;NSM;;;;;N;;;;;
@@ -10236,6 +10314,16 @@
 2E2F;VERTICAL TILDE;Lm;0;ON;;;;;N;;;;;
 2E30;RING POINT;Po;0;ON;;;;;N;;;;;
 2E31;WORD SEPARATOR MIDDLE DOT;Po;0;ON;;;;;N;;;;;
+2E32;TURNED COMMA;Po;0;ON;;;;;N;;;;;
+2E33;RAISED DOT;Po;0;ON;;;;;N;;;;;
+2E34;RAISED COMMA;Po;0;ON;;;;;N;;;;;
+2E35;TURNED SEMICOLON;Po;0;ON;;;;;N;;;;;
+2E36;DAGGER WITH LEFT GUARD;Po;0;ON;;;;;N;;;;;
+2E37;DAGGER WITH RIGHT GUARD;Po;0;ON;;;;;N;;;;;
+2E38;TURNED DAGGER;Po;0;ON;;;;;N;;;;;
+2E39;TOP HALF SECTION SIGN;Po;0;ON;;;;;N;;;;;
+2E3A;TWO-EM DASH;Pd;0;ON;;;;;N;;;;;
+2E3B;THREE-EM DASH;Pd;0;ON;;;;;N;;;;;
 2E80;CJK RADICAL REPEAT;So;0;ON;;;;;N;;;;;
 2E81;CJK RADICAL CLIFF;So;0;ON;;;;;N;;;;;
 2E82;CJK RADICAL SECOND ONE;So;0;ON;;;;;N;;;;;
@@ -10623,8 +10711,8 @@
 302B;IDEOGRAPHIC RISING TONE MARK;Mn;228;NSM;;;;;N;;;;;
 302C;IDEOGRAPHIC DEPARTING TONE MARK;Mn;232;NSM;;;;;N;;;;;
 302D;IDEOGRAPHIC ENTERING TONE MARK;Mn;222;NSM;;;;;N;;;;;
-302E;HANGUL SINGLE DOT TONE MARK;Mn;224;NSM;;;;;N;;;;;
-302F;HANGUL DOUBLE DOT TONE MARK;Mn;224;NSM;;;;;N;;;;;
+302E;HANGUL SINGLE DOT TONE MARK;Mc;224;L;;;;;N;;;;;
+302F;HANGUL DOUBLE DOT TONE MARK;Mc;224;L;;;;;N;;;;;
 3030;WAVY DASH;Pd;0;ON;;;;;N;;;;;
 3031;VERTICAL KANA REPEAT MARK;Lm;0;L;;;;;N;;;;;
 3032;VERTICAL KANA REPEAT WITH VOICED SOUND MARK;Lm;0;L;;;;;N;;;;;
@@ -11131,14 +11219,14 @@
 3245;CIRCLED IDEOGRAPH KINDERGARTEN;So;0;L;<circle> 5E7C;;;;N;;;;;
 3246;CIRCLED IDEOGRAPH SCHOOL;So;0;L;<circle> 6587;;;;N;;;;;
 3247;CIRCLED IDEOGRAPH KOTO;So;0;L;<circle> 7B8F;;;;N;;;;;
-3248;CIRCLED NUMBER TEN ON BLACK SQUARE;So;0;L;;;;;N;;;;;
-3249;CIRCLED NUMBER TWENTY ON BLACK SQUARE;So;0;L;;;;;N;;;;;
-324A;CIRCLED NUMBER THIRTY ON BLACK SQUARE;So;0;L;;;;;N;;;;;
-324B;CIRCLED NUMBER FORTY ON BLACK SQUARE;So;0;L;;;;;N;;;;;
-324C;CIRCLED NUMBER FIFTY ON BLACK SQUARE;So;0;L;;;;;N;;;;;
-324D;CIRCLED NUMBER SIXTY ON BLACK SQUARE;So;0;L;;;;;N;;;;;
-324E;CIRCLED NUMBER SEVENTY ON BLACK SQUARE;So;0;L;;;;;N;;;;;
-324F;CIRCLED NUMBER EIGHTY ON BLACK SQUARE;So;0;L;;;;;N;;;;;
+3248;CIRCLED NUMBER TEN ON BLACK SQUARE;No;0;L;;;;10;N;;;;;
+3249;CIRCLED NUMBER TWENTY ON BLACK SQUARE;No;0;L;;;;20;N;;;;;
+324A;CIRCLED NUMBER THIRTY ON BLACK SQUARE;No;0;L;;;;30;N;;;;;
+324B;CIRCLED NUMBER FORTY ON BLACK SQUARE;No;0;L;;;;40;N;;;;;
+324C;CIRCLED NUMBER FIFTY ON BLACK SQUARE;No;0;L;;;;50;N;;;;;
+324D;CIRCLED NUMBER SIXTY ON BLACK SQUARE;No;0;L;;;;60;N;;;;;
+324E;CIRCLED NUMBER SEVENTY ON BLACK SQUARE;No;0;L;;;;70;N;;;;;
+324F;CIRCLED NUMBER EIGHTY ON BLACK SQUARE;No;0;L;;;;80;N;;;;;
 3250;PARTNERSHIP SIGN;So;0;ON;<square> 0050 0054 0045;;;;N;;;;;
 3251;CIRCLED NUMBER TWENTY ONE;No;0;ON;<circle> 0032 0031;;;21;N;;;;;
 3252;CIRCLED NUMBER TWENTY TWO;No;0;ON;<circle> 0032 0032;;;22;N;;;;;
@@ -11637,7 +11725,7 @@
 4DFE;HEXAGRAM FOR AFTER COMPLETION;So;0;ON;;;;;N;;;;;
 4DFF;HEXAGRAM FOR BEFORE COMPLETION;So;0;ON;;;;;N;;;;;
 4E00;<CJK Ideograph, First>;Lo;0;L;;;;;N;;;;;
-9FCB;<CJK Ideograph, Last>;Lo;0;L;;;;;N;;;;;
+9FCC;<CJK Ideograph, Last>;Lo;0;L;;;;;N;;;;;
 A000;YI SYLLABLE IT;Lo;0;L;;;;;N;;;;;
 A001;YI SYLLABLE IX;Lo;0;L;;;;;N;;;;;
 A002;YI SYLLABLE I;Lo;0;L;;;;;N;;;;;
@@ -13258,6 +13346,14 @@ A670;COMBINING CYRILLIC TEN MILLIONS SIGN;Me;0;NSM;;;;;N;;;;;
 A671;COMBINING CYRILLIC HUNDRED MILLIONS SIGN;Me;0;NSM;;;;;N;;;;;
 A672;COMBINING CYRILLIC THOUSAND MILLIONS SIGN;Me;0;NSM;;;;;N;;;;;
 A673;SLAVONIC ASTERISK;Po;0;ON;;;;;N;;;;;
+A674;COMBINING CYRILLIC LETTER UKRAINIAN IE;Mn;230;NSM;;;;;N;;;;;
+A675;COMBINING CYRILLIC LETTER I;Mn;230;NSM;;;;;N;;;;;
+A676;COMBINING CYRILLIC LETTER YI;Mn;230;NSM;;;;;N;;;;;
+A677;COMBINING CYRILLIC LETTER U;Mn;230;NSM;;;;;N;;;;;
+A678;COMBINING CYRILLIC LETTER HARD SIGN;Mn;230;NSM;;;;;N;;;;;
+A679;COMBINING CYRILLIC LETTER YERU;Mn;230;NSM;;;;;N;;;;;
+A67A;COMBINING CYRILLIC LETTER SOFT SIGN;Mn;230;NSM;;;;;N;;;;;
+A67B;COMBINING CYRILLIC LETTER OMEGA;Mn;230;NSM;;;;;N;;;;;
 A67C;COMBINING CYRILLIC KAVYKA;Mn;230;NSM;;;;;N;;;;;
 A67D;COMBINING CYRILLIC PAYEROK;Mn;230;NSM;;;;;N;;;;;
 A67E;CYRILLIC KAVYKA;Po;0;ON;;;;;N;;;;;
@@ -13286,6 +13382,7 @@ A694;CYRILLIC CAPITAL LETTER HWE;Lu;0;L;;;;;N;;;;A695;
 A695;CYRILLIC SMALL LETTER HWE;Ll;0;L;;;;;N;;;A694;;A694
 A696;CYRILLIC CAPITAL LETTER SHWE;Lu;0;L;;;;;N;;;;A697;
 A697;CYRILLIC SMALL LETTER SHWE;Ll;0;L;;;;;N;;;A696;;A696
+A69F;COMBINING CYRILLIC LETTER IOTIFIED E;Mn;230;NSM;;;;;N;;;;;
 A6A0;BAMUM LETTER A;Lo;0;L;;;;;N;;;;;
 A6A1;BAMUM LETTER KA;Lo;0;L;;;;;N;;;;;
 A6A2;BAMUM LETTER U;Lo;0;L;;;;;N;;;;;
@@ -13519,6 +13616,8 @@ A78D;LATIN CAPITAL LETTER TURNED H;Lu;0;L;;;;;N;;;;0265;
 A78E;LATIN SMALL LETTER L WITH RETROFLEX HOOK AND BELT;Ll;0;L;;;;;N;;;;;
 A790;LATIN CAPITAL LETTER N WITH DESCENDER;Lu;0;L;;;;;N;;;;A791;
 A791;LATIN SMALL LETTER N WITH DESCENDER;Ll;0;L;;;;;N;;;A790;;A790
+A792;LATIN CAPITAL LETTER C WITH BAR;Lu;0;L;;;;;N;;;;A793;
+A793;LATIN SMALL LETTER C WITH BAR;Ll;0;L;;;;;N;;;A792;;A792
 A7A0;LATIN CAPITAL LETTER G WITH OBLIQUE STROKE;Lu;0;L;;;;;N;;;;A7A1;
 A7A1;LATIN SMALL LETTER G WITH OBLIQUE STROKE;Ll;0;L;;;;;N;;;A7A0;;A7A0
 A7A2;LATIN CAPITAL LETTER K WITH OBLIQUE STROKE;Lu;0;L;;;;;N;;;;A7A3;
@@ -13529,6 +13628,9 @@ A7A6;LATIN CAPITAL LETTER R WITH OBLIQUE STROKE;Lu;0;L;;;;;N;;;;A7A7;
 A7A7;LATIN SMALL LETTER R WITH OBLIQUE STROKE;Ll;0;L;;;;;N;;;A7A6;;A7A6
 A7A8;LATIN CAPITAL LETTER S WITH OBLIQUE STROKE;Lu;0;L;;;;;N;;;;A7A9;
 A7A9;LATIN SMALL LETTER S WITH OBLIQUE STROKE;Ll;0;L;;;;;N;;;A7A8;;A7A8
+A7AA;LATIN CAPITAL LETTER H WITH HOOK;Lu;0;L;;;;;N;;;;0266;
+A7F8;MODIFIER LETTER CAPITAL H WITH STROKE;Lm;0;L;<super> 0126;;;;N;;;;;
+A7F9;MODIFIER LETTER SMALL LIGATURE OE;Lm;0;L;<super> 0153;;;;N;;;;;
 A7FA;LATIN LETTER SMALL CAPITAL TURNED M;Ll;0;L;;;;;N;;;;;
 A7FB;LATIN EPIGRAPHIC LETTER REVERSED F;Lo;0;L;;;;;N;;;;;
 A7FC;LATIN EPIGRAPHIC LETTER REVERSED P;Lo;0;L;;;;;N;;;;;
@@ -14142,6 +14244,29 @@ AADC;TAI VIET SYMBOL NUENG;Lo;0;L;;;;;N;;;;;
 AADD;TAI VIET SYMBOL SAM;Lm;0;L;;;;;N;;;;;
 AADE;TAI VIET SYMBOL HO HOI;Po;0;L;;;;;N;;;;;
 AADF;TAI VIET SYMBOL KOI KOI;Po;0;L;;;;;N;;;;;
+AAE0;MEETEI MAYEK LETTER E;Lo;0;L;;;;;N;;;;;
+AAE1;MEETEI MAYEK LETTER O;Lo;0;L;;;;;N;;;;;
+AAE2;MEETEI MAYEK LETTER CHA;Lo;0;L;;;;;N;;;;;
+AAE3;MEETEI MAYEK LETTER NYA;Lo;0;L;;;;;N;;;;;
+AAE4;MEETEI MAYEK LETTER TTA;Lo;0;L;;;;;N;;;;;
+AAE5;MEETEI MAYEK LETTER TTHA;Lo;0;L;;;;;N;;;;;
+AAE6;MEETEI MAYEK LETTER DDA;Lo;0;L;;;;;N;;;;;
+AAE7;MEETEI MAYEK LETTER DDHA;Lo;0;L;;;;;N;;;;;
+AAE8;MEETEI MAYEK LETTER NNA;Lo;0;L;;;;;N;;;;;
+AAE9;MEETEI MAYEK LETTER SHA;Lo;0;L;;;;;N;;;;;
+AAEA;MEETEI MAYEK LETTER SSA;Lo;0;L;;;;;N;;;;;
+AAEB;MEETEI MAYEK VOWEL SIGN II;Mc;0;L;;;;;N;;;;;
+AAEC;MEETEI MAYEK VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;;
+AAED;MEETEI MAYEK VOWEL SIGN AAI;Mn;0;NSM;;;;;N;;;;;
+AAEE;MEETEI MAYEK VOWEL SIGN AU;Mc;0;L;;;;;N;;;;;
+AAEF;MEETEI MAYEK VOWEL SIGN AAU;Mc;0;L;;;;;N;;;;;
+AAF0;MEETEI MAYEK CHEIKHAN;Po;0;L;;;;;N;;;;;
+AAF1;MEETEI MAYEK AHANG KHUDAM;Po;0;L;;;;;N;;;;;
+AAF2;MEETEI MAYEK ANJI;Lo;0;L;;;;;N;;;;;
+AAF3;MEETEI MAYEK SYLLABLE REPETITION MARK;Lm;0;L;;;;;N;;;;;
+AAF4;MEETEI MAYEK WORD REPETITION MARK;Lm;0;L;;;;;N;;;;;
+AAF5;MEETEI MAYEK VOWEL SIGN VISARGA;Mc;0;L;;;;;N;;;;;
+AAF6;MEETEI MAYEK VIRAMA;Mn;9;NSM;;;;;N;;;;;
 AB01;ETHIOPIC SYLLABLE TTHU;Lo;0;L;;;;;N;;;;;
 AB02;ETHIOPIC SYLLABLE TTHI;Lo;0;L;;;;;N;;;;;
 AB03;ETHIOPIC SYLLABLE TTHAA;Lo;0;L;;;;;N;;;;;
@@ -14614,6 +14739,8 @@ FA2A;CJK COMPATIBILITY IDEOGRAPH-FA2A;Lo;0;L;98EF;;;;N;;;;;
 FA2B;CJK COMPATIBILITY IDEOGRAPH-FA2B;Lo;0;L;98FC;;;;N;;;;;
 FA2C;CJK COMPATIBILITY IDEOGRAPH-FA2C;Lo;0;L;9928;;;;N;;;;;
 FA2D;CJK COMPATIBILITY IDEOGRAPH-FA2D;Lo;0;L;9DB4;;;;N;;;;;
+FA2E;CJK COMPATIBILITY IDEOGRAPH-FA2E;Lo;0;L;90DE;;;;N;;;;;
+FA2F;CJK COMPATIBILITY IDEOGRAPH-FA2F;Lo;0;L;96B7;;;;N;;;;;
 FA30;CJK COMPATIBILITY IDEOGRAPH-FA30;Lo;0;L;4FAE;;;;N;;;;;
 FA31;CJK COMPATIBILITY IDEOGRAPH-FA31;Lo;0;L;50E7;;;;N;;;;;
 FA32;CJK COMPATIBILITY IDEOGRAPH-FA32;Lo;0;L;514D;;;;N;;;;;
@@ -16126,7 +16253,7 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
 100FA;LINEAR B IDEOGRAM VESSEL B305;Lo;0;L;;;;;N;;;;;
 10100;AEGEAN WORD SEPARATOR LINE;Po;0;L;;;;;N;;;;;
 10101;AEGEAN WORD SEPARATOR DOT;Po;0;ON;;;;;N;;;;;
-10102;AEGEAN CHECK MARK;So;0;L;;;;;N;;;;;
+10102;AEGEAN CHECK MARK;Po;0;L;;;;;N;;;;;
 10107;AEGEAN NUMBER ONE;No;0;L;;;;1;N;;;;;
 10108;AEGEAN NUMBER TWO;No;0;L;;;;2;N;;;;;
 10109;AEGEAN NUMBER THREE;No;0;L;;;;3;N;;;;;
@@ -16845,6 +16972,64 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
 10938;LYDIAN LETTER NN;Lo;0;R;;;;;N;;;;;
 10939;LYDIAN LETTER C;Lo;0;R;;;;;N;;;;;
 1093F;LYDIAN TRIANGULAR MARK;Po;0;R;;;;;N;;;;;
+10980;MEROITIC HIEROGLYPHIC LETTER A;Lo;0;R;;;;;N;;;;;
+10981;MEROITIC HIEROGLYPHIC LETTER E;Lo;0;R;;;;;N;;;;;
+10982;MEROITIC HIEROGLYPHIC LETTER I;Lo;0;R;;;;;N;;;;;
+10983;MEROITIC HIEROGLYPHIC LETTER O;Lo;0;R;;;;;N;;;;;
+10984;MEROITIC HIEROGLYPHIC LETTER YA;Lo;0;R;;;;;N;;;;;
+10985;MEROITIC HIEROGLYPHIC LETTER WA;Lo;0;R;;;;;N;;;;;
+10986;MEROITIC HIEROGLYPHIC LETTER BA;Lo;0;R;;;;;N;;;;;
+10987;MEROITIC HIEROGLYPHIC LETTER BA-2;Lo;0;R;;;;;N;;;;;
+10988;MEROITIC HIEROGLYPHIC LETTER PA;Lo;0;R;;;;;N;;;;;
+10989;MEROITIC HIEROGLYPHIC LETTER MA;Lo;0;R;;;;;N;;;;;
+1098A;MEROITIC HIEROGLYPHIC LETTER NA;Lo;0;R;;;;;N;;;;;
+1098B;MEROITIC HIEROGLYPHIC LETTER NA-2;Lo;0;R;;;;;N;;;;;
+1098C;MEROITIC HIEROGLYPHIC LETTER NE;Lo;0;R;;;;;N;;;;;
+1098D;MEROITIC HIEROGLYPHIC LETTER NE-2;Lo;0;R;;;;;N;;;;;
+1098E;MEROITIC HIEROGLYPHIC LETTER RA;Lo;0;R;;;;;N;;;;;
+1098F;MEROITIC HIEROGLYPHIC LETTER RA-2;Lo;0;R;;;;;N;;;;;
+10990;MEROITIC HIEROGLYPHIC LETTER LA;Lo;0;R;;;;;N;;;;;
+10991;MEROITIC HIEROGLYPHIC LETTER KHA;Lo;0;R;;;;;N;;;;;
+10992;MEROITIC HIEROGLYPHIC LETTER HHA;Lo;0;R;;;;;N;;;;;
+10993;MEROITIC HIEROGLYPHIC LETTER SA;Lo;0;R;;;;;N;;;;;
+10994;MEROITIC HIEROGLYPHIC LETTER SA-2;Lo;0;R;;;;;N;;;;;
+10995;MEROITIC HIEROGLYPHIC LETTER SE;Lo;0;R;;;;;N;;;;;
+10996;MEROITIC HIEROGLYPHIC LETTER KA;Lo;0;R;;;;;N;;;;;
+10997;MEROITIC HIEROGLYPHIC LETTER QA;Lo;0;R;;;;;N;;;;;
+10998;MEROITIC HIEROGLYPHIC LETTER TA;Lo;0;R;;;;;N;;;;;
+10999;MEROITIC HIEROGLYPHIC LETTER TA-2;Lo;0;R;;;;;N;;;;;
+1099A;MEROITIC HIEROGLYPHIC LETTER TE;Lo;0;R;;;;;N;;;;;
+1099B;MEROITIC HIEROGLYPHIC LETTER TE-2;Lo;0;R;;;;;N;;;;;
+1099C;MEROITIC HIEROGLYPHIC LETTER TO;Lo;0;R;;;;;N;;;;;
+1099D;MEROITIC HIEROGLYPHIC LETTER DA;Lo;0;R;;;;;N;;;;;
+1099E;MEROITIC HIEROGLYPHIC SYMBOL VIDJ;Lo;0;R;;;;;N;;;;;
+1099F;MEROITIC HIEROGLYPHIC SYMBOL VIDJ-2;Lo;0;R;;;;;N;;;;;
+109A0;MEROITIC CURSIVE LETTER A;Lo;0;R;;;;;N;;;;;
+109A1;MEROITIC CURSIVE LETTER E;Lo;0;R;;;;;N;;;;;
+109A2;MEROITIC CURSIVE LETTER I;Lo;0;R;;;;;N;;;;;
+109A3;MEROITIC CURSIVE LETTER O;Lo;0;R;;;;;N;;;;;
+109A4;MEROITIC CURSIVE LETTER YA;Lo;0;R;;;;;N;;;;;
+109A5;MEROITIC CURSIVE LETTER WA;Lo;0;R;;;;;N;;;;;
+109A6;MEROITIC CURSIVE LETTER BA;Lo;0;R;;;;;N;;;;;
+109A7;MEROITIC CURSIVE LETTER PA;Lo;0;R;;;;;N;;;;;
+109A8;MEROITIC CURSIVE LETTER MA;Lo;0;R;;;;;N;;;;;
+109A9;MEROITIC CURSIVE LETTER NA;Lo;0;R;;;;;N;;;;;
+109AA;MEROITIC CURSIVE LETTER NE;Lo;0;R;;;;;N;;;;;
+109AB;MEROITIC CURSIVE LETTER RA;Lo;0;R;;;;;N;;;;;
+109AC;MEROITIC CURSIVE LETTER LA;Lo;0;R;;;;;N;;;;;
+109AD;MEROITIC CURSIVE LETTER KHA;Lo;0;R;;;;;N;;;;;
+109AE;MEROITIC CURSIVE LETTER HHA;Lo;0;R;;;;;N;;;;;
+109AF;MEROITIC CURSIVE LETTER SA;Lo;0;R;;;;;N;;;;;
+109B0;MEROITIC CURSIVE LETTER ARCHAIC SA;Lo;0;R;;;;;N;;;;;
+109B1;MEROITIC CURSIVE LETTER SE;Lo;0;R;;;;;N;;;;;
+109B2;MEROITIC CURSIVE LETTER KA;Lo;0;R;;;;;N;;;;;
+109B3;MEROITIC CURSIVE LETTER QA;Lo;0;R;;;;;N;;;;;
+109B4;MEROITIC CURSIVE LETTER TA;Lo;0;R;;;;;N;;;;;
+109B5;MEROITIC CURSIVE LETTER TE;Lo;0;R;;;;;N;;;;;
+109B6;MEROITIC CURSIVE LETTER TO;Lo;0;R;;;;;N;;;;;
+109B7;MEROITIC CURSIVE LETTER DA;Lo;0;R;;;;;N;;;;;
+109BE;MEROITIC CURSIVE LOGOGRAM RMT;Lo;0;R;;;;;N;;;;;
+109BF;MEROITIC CURSIVE LOGOGRAM IMN;Lo;0;R;;;;;N;;;;;
 10A00;KHAROSHTHI LETTER A;Lo;0;R;;;;;N;;;;;
 10A01;KHAROSHTHI VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;;
 10A02;KHAROSHTHI VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
@@ -17338,6 +17523,257 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
 110BF;KAITHI DOUBLE SECTION MARK;Po;0;L;;;;;N;;;;;
 110C0;KAITHI DANDA;Po;0;L;;;;;N;;;;;
 110C1;KAITHI DOUBLE DANDA;Po;0;L;;;;;N;;;;;
+110D0;SORA SOMPENG LETTER SAH;Lo;0;L;;;;;N;;;;;
+110D1;SORA SOMPENG LETTER TAH;Lo;0;L;;;;;N;;;;;
+110D2;SORA SOMPENG LETTER BAH;Lo;0;L;;;;;N;;;;;
+110D3;SORA SOMPENG LETTER CAH;Lo;0;L;;;;;N;;;;;
+110D4;SORA SOMPENG LETTER DAH;Lo;0;L;;;;;N;;;;;
+110D5;SORA SOMPENG LETTER GAH;Lo;0;L;;;;;N;;;;;
+110D6;SORA SOMPENG LETTER MAH;Lo;0;L;;;;;N;;;;;
+110D7;SORA SOMPENG LETTER NGAH;Lo;0;L;;;;;N;;;;;
+110D8;SORA SOMPENG LETTER LAH;Lo;0;L;;;;;N;;;;;
+110D9;SORA SOMPENG LETTER NAH;Lo;0;L;;;;;N;;;;;
+110DA;SORA SOMPENG LETTER VAH;Lo;0;L;;;;;N;;;;;
+110DB;SORA SOMPENG LETTER PAH;Lo;0;L;;;;;N;;;;;
+110DC;SORA SOMPENG LETTER YAH;Lo;0;L;;;;;N;;;;;
+110DD;SORA SOMPENG LETTER RAH;Lo;0;L;;;;;N;;;;;
+110DE;SORA SOMPENG LETTER HAH;Lo;0;L;;;;;N;;;;;
+110DF;SORA SOMPENG LETTER KAH;Lo;0;L;;;;;N;;;;;
+110E0;SORA SOMPENG LETTER JAH;Lo;0;L;;;;;N;;;;;
+110E1;SORA SOMPENG LETTER NYAH;Lo;0;L;;;;;N;;;;;
+110E2;SORA SOMPENG LETTER AH;Lo;0;L;;;;;N;;;;;
+110E3;SORA SOMPENG LETTER EEH;Lo;0;L;;;;;N;;;;;
+110E4;SORA SOMPENG LETTER IH;Lo;0;L;;;;;N;;;;;
+110E5;SORA SOMPENG LETTER UH;Lo;0;L;;;;;N;;;;;
+110E6;SORA SOMPENG LETTER OH;Lo;0;L;;;;;N;;;;;
+110E7;SORA SOMPENG LETTER EH;Lo;0;L;;;;;N;;;;;
+110E8;SORA SOMPENG LETTER MAE;Lo;0;L;;;;;N;;;;;
+110F0;SORA SOMPENG DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+110F1;SORA SOMPENG DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+110F2;SORA SOMPENG DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+110F3;SORA SOMPENG DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+110F4;SORA SOMPENG DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+110F5;SORA SOMPENG DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+110F6;SORA SOMPENG DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+110F7;SORA SOMPENG DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+110F8;SORA SOMPENG DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+110F9;SORA SOMPENG DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+11100;CHAKMA SIGN CANDRABINDU;Mn;230;NSM;;;;;N;;;;;
+11101;CHAKMA SIGN ANUSVARA;Mn;230;NSM;;;;;N;;;;;
+11102;CHAKMA SIGN VISARGA;Mn;230;NSM;;;;;N;;;;;
+11103;CHAKMA LETTER AA;Lo;0;L;;;;;N;;;;;
+11104;CHAKMA LETTER I;Lo;0;L;;;;;N;;;;;
+11105;CHAKMA LETTER U;Lo;0;L;;;;;N;;;;;
+11106;CHAKMA LETTER E;Lo;0;L;;;;;N;;;;;
+11107;CHAKMA LETTER KAA;Lo;0;L;;;;;N;;;;;
+11108;CHAKMA LETTER KHAA;Lo;0;L;;;;;N;;;;;
+11109;CHAKMA LETTER GAA;Lo;0;L;;;;;N;;;;;
+1110A;CHAKMA LETTER GHAA;Lo;0;L;;;;;N;;;;;
+1110B;CHAKMA LETTER NGAA;Lo;0;L;;;;;N;;;;;
+1110C;CHAKMA LETTER CAA;Lo;0;L;;;;;N;;;;;
+1110D;CHAKMA LETTER CHAA;Lo;0;L;;;;;N;;;;;
+1110E;CHAKMA LETTER JAA;Lo;0;L;;;;;N;;;;;
+1110F;CHAKMA LETTER JHAA;Lo;0;L;;;;;N;;;;;
+11110;CHAKMA LETTER NYAA;Lo;0;L;;;;;N;;;;;
+11111;CHAKMA LETTER TTAA;Lo;0;L;;;;;N;;;;;
+11112;CHAKMA LETTER TTHAA;Lo;0;L;;;;;N;;;;;
+11113;CHAKMA LETTER DDAA;Lo;0;L;;;;;N;;;;;
+11114;CHAKMA LETTER DDHAA;Lo;0;L;;;;;N;;;;;
+11115;CHAKMA LETTER NNAA;Lo;0;L;;;;;N;;;;;
+11116;CHAKMA LETTER TAA;Lo;0;L;;;;;N;;;;;
+11117;CHAKMA LETTER THAA;Lo;0;L;;;;;N;;;;;
+11118;CHAKMA LETTER DAA;Lo;0;L;;;;;N;;;;;
+11119;CHAKMA LETTER DHAA;Lo;0;L;;;;;N;;;;;
+1111A;CHAKMA LETTER NAA;Lo;0;L;;;;;N;;;;;
+1111B;CHAKMA LETTER PAA;Lo;0;L;;;;;N;;;;;
+1111C;CHAKMA LETTER PHAA;Lo;0;L;;;;;N;;;;;
+1111D;CHAKMA LETTER BAA;Lo;0;L;;;;;N;;;;;
+1111E;CHAKMA LETTER BHAA;Lo;0;L;;;;;N;;;;;
+1111F;CHAKMA LETTER MAA;Lo;0;L;;;;;N;;;;;
+11120;CHAKMA LETTER YYAA;Lo;0;L;;;;;N;;;;;
+11121;CHAKMA LETTER YAA;Lo;0;L;;;;;N;;;;;
+11122;CHAKMA LETTER RAA;Lo;0;L;;;;;N;;;;;
+11123;CHAKMA LETTER LAA;Lo;0;L;;;;;N;;;;;
+11124;CHAKMA LETTER WAA;Lo;0;L;;;;;N;;;;;
+11125;CHAKMA LETTER SAA;Lo;0;L;;;;;N;;;;;
+11126;CHAKMA LETTER HAA;Lo;0;L;;;;;N;;;;;
+11127;CHAKMA VOWEL SIGN A;Mn;0;NSM;;;;;N;;;;;
+11128;CHAKMA VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;;
+11129;CHAKMA VOWEL SIGN II;Mn;0;NSM;;;;;N;;;;;
+1112A;CHAKMA VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+1112B;CHAKMA VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;;
+1112C;CHAKMA VOWEL SIGN E;Mc;0;L;;;;;N;;;;;
+1112D;CHAKMA VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;;
+1112E;CHAKMA VOWEL SIGN O;Mn;0;NSM;11131 11127;;;;N;;;;;
+1112F;CHAKMA VOWEL SIGN AU;Mn;0;NSM;11132 11127;;;;N;;;;;
+11130;CHAKMA VOWEL SIGN OI;Mn;0;NSM;;;;;N;;;;;
+11131;CHAKMA O MARK;Mn;0;NSM;;;;;N;;;;;
+11132;CHAKMA AU MARK;Mn;0;NSM;;;;;N;;;;;
+11133;CHAKMA VIRAMA;Mn;9;NSM;;;;;N;;;;;
+11134;CHAKMA MAAYYAA;Mn;9;NSM;;;;;N;;;;;
+11136;CHAKMA DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+11137;CHAKMA DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+11138;CHAKMA DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+11139;CHAKMA DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+1113A;CHAKMA DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+1113B;CHAKMA DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+1113C;CHAKMA DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+1113D;CHAKMA DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+1113E;CHAKMA DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+1113F;CHAKMA DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+11140;CHAKMA SECTION MARK;Po;0;L;;;;;N;;;;;
+11141;CHAKMA DANDA;Po;0;L;;;;;N;;;;;
+11142;CHAKMA DOUBLE DANDA;Po;0;L;;;;;N;;;;;
+11143;CHAKMA QUESTION MARK;Po;0;L;;;;;N;;;;;
+11180;SHARADA SIGN CANDRABINDU;Mn;0;NSM;;;;;N;;;;;
+11181;SHARADA SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;;
+11182;SHARADA SIGN VISARGA;Mc;0;L;;;;;N;;;;;
+11183;SHARADA LETTER A;Lo;0;L;;;;;N;;;;;
+11184;SHARADA LETTER AA;Lo;0;L;;;;;N;;;;;
+11185;SHARADA LETTER I;Lo;0;L;;;;;N;;;;;
+11186;SHARADA LETTER II;Lo;0;L;;;;;N;;;;;
+11187;SHARADA LETTER U;Lo;0;L;;;;;N;;;;;
+11188;SHARADA LETTER UU;Lo;0;L;;;;;N;;;;;
+11189;SHARADA LETTER VOCALIC R;Lo;0;L;;;;;N;;;;;
+1118A;SHARADA LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;;
+1118B;SHARADA LETTER VOCALIC L;Lo;0;L;;;;;N;;;;;
+1118C;SHARADA LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;;
+1118D;SHARADA LETTER E;Lo;0;L;;;;;N;;;;;
+1118E;SHARADA LETTER AI;Lo;0;L;;;;;N;;;;;
+1118F;SHARADA LETTER O;Lo;0;L;;;;;N;;;;;
+11190;SHARADA LETTER AU;Lo;0;L;;;;;N;;;;;
+11191;SHARADA LETTER KA;Lo;0;L;;;;;N;;;;;
+11192;SHARADA LETTER KHA;Lo;0;L;;;;;N;;;;;
+11193;SHARADA LETTER GA;Lo;0;L;;;;;N;;;;;
+11194;SHARADA LETTER GHA;Lo;0;L;;;;;N;;;;;
+11195;SHARADA LETTER NGA;Lo;0;L;;;;;N;;;;;
+11196;SHARADA LETTER CA;Lo;0;L;;;;;N;;;;;
+11197;SHARADA LETTER CHA;Lo;0;L;;;;;N;;;;;
+11198;SHARADA LETTER JA;Lo;0;L;;;;;N;;;;;
+11199;SHARADA LETTER JHA;Lo;0;L;;;;;N;;;;;
+1119A;SHARADA LETTER NYA;Lo;0;L;;;;;N;;;;;
+1119B;SHARADA LETTER TTA;Lo;0;L;;;;;N;;;;;
+1119C;SHARADA LETTER TTHA;Lo;0;L;;;;;N;;;;;
+1119D;SHARADA LETTER DDA;Lo;0;L;;;;;N;;;;;
+1119E;SHARADA LETTER DDHA;Lo;0;L;;;;;N;;;;;
+1119F;SHARADA LETTER NNA;Lo;0;L;;;;;N;;;;;
+111A0;SHARADA LETTER TA;Lo;0;L;;;;;N;;;;;
+111A1;SHARADA LETTER THA;Lo;0;L;;;;;N;;;;;
+111A2;SHARADA LETTER DA;Lo;0;L;;;;;N;;;;;
+111A3;SHARADA LETTER DHA;Lo;0;L;;;;;N;;;;;
+111A4;SHARADA LETTER NA;Lo;0;L;;;;;N;;;;;
+111A5;SHARADA LETTER PA;Lo;0;L;;;;;N;;;;;
+111A6;SHARADA LETTER PHA;Lo;0;L;;;;;N;;;;;
+111A7;SHARADA LETTER BA;Lo;0;L;;;;;N;;;;;
+111A8;SHARADA LETTER BHA;Lo;0;L;;;;;N;;;;;
+111A9;SHARADA LETTER MA;Lo;0;L;;;;;N;;;;;
+111AA;SHARADA LETTER YA;Lo;0;L;;;;;N;;;;;
+111AB;SHARADA LETTER RA;Lo;0;L;;;;;N;;;;;
+111AC;SHARADA LETTER LA;Lo;0;L;;;;;N;;;;;
+111AD;SHARADA LETTER LLA;Lo;0;L;;;;;N;;;;;
+111AE;SHARADA LETTER VA;Lo;0;L;;;;;N;;;;;
+111AF;SHARADA LETTER SHA;Lo;0;L;;;;;N;;;;;
+111B0;SHARADA LETTER SSA;Lo;0;L;;;;;N;;;;;
+111B1;SHARADA LETTER SA;Lo;0;L;;;;;N;;;;;
+111B2;SHARADA LETTER HA;Lo;0;L;;;;;N;;;;;
+111B3;SHARADA VOWEL SIGN AA;Mc;0;L;;;;;N;;;;;
+111B4;SHARADA VOWEL SIGN I;Mc;0;L;;;;;N;;;;;
+111B5;SHARADA VOWEL SIGN II;Mc;0;L;;;;;N;;;;;
+111B6;SHARADA VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+111B7;SHARADA VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;;
+111B8;SHARADA VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;;
+111B9;SHARADA VOWEL SIGN VOCALIC RR;Mn;0;NSM;;;;;N;;;;;
+111BA;SHARADA VOWEL SIGN VOCALIC L;Mn;0;NSM;;;;;N;;;;;
+111BB;SHARADA VOWEL SIGN VOCALIC LL;Mn;0;NSM;;;;;N;;;;;
+111BC;SHARADA VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;;
+111BD;SHARADA VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;;
+111BE;SHARADA VOWEL SIGN O;Mn;0;NSM;;;;;N;;;;;
+111BF;SHARADA VOWEL SIGN AU;Mc;0;L;;;;;N;;;;;
+111C0;SHARADA SIGN VIRAMA;Mc;9;L;;;;;N;;;;;
+111C1;SHARADA SIGN AVAGRAHA;Lo;0;L;;;;;N;;;;;
+111C2;SHARADA SIGN JIHVAMULIYA;Lo;0;L;;;;;N;;;;;
+111C3;SHARADA SIGN UPADHMANIYA;Lo;0;L;;;;;N;;;;;
+111C4;SHARADA OM;Lo;0;L;;;;;N;;;;;
+111C5;SHARADA DANDA;Po;0;L;;;;;N;;;;;
+111C6;SHARADA DOUBLE DANDA;Po;0;L;;;;;N;;;;;
+111C7;SHARADA ABBREVIATION SIGN;Po;0;L;;;;;N;;;;;
+111C8;SHARADA SEPARATOR;Po;0;L;;;;;N;;;;;
+111D0;SHARADA DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+111D1;SHARADA DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+111D2;SHARADA DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+111D3;SHARADA DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+111D4;SHARADA DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+111D5;SHARADA DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+111D6;SHARADA DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+111D7;SHARADA DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+111D8;SHARADA DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+111D9;SHARADA DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+11680;TAKRI LETTER A;Lo;0;L;;;;;N;;;;;
+11681;TAKRI LETTER AA;Lo;0;L;;;;;N;;;;;
+11682;TAKRI LETTER I;Lo;0;L;;;;;N;;;;;
+11683;TAKRI LETTER II;Lo;0;L;;;;;N;;;;;
+11684;TAKRI LETTER U;Lo;0;L;;;;;N;;;;;
+11685;TAKRI LETTER UU;Lo;0;L;;;;;N;;;;;
+11686;TAKRI LETTER E;Lo;0;L;;;;;N;;;;;
+11687;TAKRI LETTER AI;Lo;0;L;;;;;N;;;;;
+11688;TAKRI LETTER O;Lo;0;L;;;;;N;;;;;
+11689;TAKRI LETTER AU;Lo;0;L;;;;;N;;;;;
+1168A;TAKRI LETTER KA;Lo;0;L;;;;;N;;;;;
+1168B;TAKRI LETTER KHA;Lo;0;L;;;;;N;;;;;
+1168C;TAKRI LETTER GA;Lo;0;L;;;;;N;;;;;
+1168D;TAKRI LETTER GHA;Lo;0;L;;;;;N;;;;;
+1168E;TAKRI LETTER NGA;Lo;0;L;;;;;N;;;;;
+1168F;TAKRI LETTER CA;Lo;0;L;;;;;N;;;;;
+11690;TAKRI LETTER CHA;Lo;0;L;;;;;N;;;;;
+11691;TAKRI LETTER JA;Lo;0;L;;;;;N;;;;;
+11692;TAKRI LETTER JHA;Lo;0;L;;;;;N;;;;;
+11693;TAKRI LETTER NYA;Lo;0;L;;;;;N;;;;;
+11694;TAKRI LETTER TTA;Lo;0;L;;;;;N;;;;;
+11695;TAKRI LETTER TTHA;Lo;0;L;;;;;N;;;;;
+11696;TAKRI LETTER DDA;Lo;0;L;;;;;N;;;;;
+11697;TAKRI LETTER DDHA;Lo;0;L;;;;;N;;;;;
+11698;TAKRI LETTER NNA;Lo;0;L;;;;;N;;;;;
+11699;TAKRI LETTER TA;Lo;0;L;;;;;N;;;;;
+1169A;TAKRI LETTER THA;Lo;0;L;;;;;N;;;;;
+1169B;TAKRI LETTER DA;Lo;0;L;;;;;N;;;;;
+1169C;TAKRI LETTER DHA;Lo;0;L;;;;;N;;;;;
+1169D;TAKRI LETTER NA;Lo;0;L;;;;;N;;;;;
+1169E;TAKRI LETTER PA;Lo;0;L;;;;;N;;;;;
+1169F;TAKRI LETTER PHA;Lo;0;L;;;;;N;;;;;
+116A0;TAKRI LETTER BA;Lo;0;L;;;;;N;;;;;
+116A1;TAKRI LETTER BHA;Lo;0;L;;;;;N;;;;;
+116A2;TAKRI LETTER MA;Lo;0;L;;;;;N;;;;;
+116A3;TAKRI LETTER YA;Lo;0;L;;;;;N;;;;;
+116A4;TAKRI LETTER RA;Lo;0;L;;;;;N;;;;;
+116A5;TAKRI LETTER LA;Lo;0;L;;;;;N;;;;;
+116A6;TAKRI LETTER VA;Lo;0;L;;;;;N;;;;;
+116A7;TAKRI LETTER SHA;Lo;0;L;;;;;N;;;;;
+116A8;TAKRI LETTER SA;Lo;0;L;;;;;N;;;;;
+116A9;TAKRI LETTER HA;Lo;0;L;;;;;N;;;;;
+116AA;TAKRI LETTER RRA;Lo;0;L;;;;;N;;;;;
+116AB;TAKRI SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;;
+116AC;TAKRI SIGN VISARGA;Mc;0;L;;;;;N;;;;;
+116AD;TAKRI VOWEL SIGN AA;Mn;0;NSM;;;;;N;;;;;
+116AE;TAKRI VOWEL SIGN I;Mc;0;L;;;;;N;;;;;
+116AF;TAKRI VOWEL SIGN II;Mc;0;L;;;;;N;;;;;
+116B0;TAKRI VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+116B1;TAKRI VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;;
+116B2;TAKRI VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;;
+116B3;TAKRI VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;;
+116B4;TAKRI VOWEL SIGN O;Mn;0;NSM;;;;;N;;;;;
+116B5;TAKRI VOWEL SIGN AU;Mn;0;NSM;;;;;N;;;;;
+116B6;TAKRI SIGN VIRAMA;Mc;9;L;;;;;N;;;;;
+116B7;TAKRI SIGN NUKTA;Mn;7;NSM;;;;;N;;;;;
+116C0;TAKRI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+116C1;TAKRI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+116C2;TAKRI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+116C3;TAKRI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+116C4;TAKRI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+116C5;TAKRI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+116C6;TAKRI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+116C7;TAKRI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+116C8;TAKRI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+116C9;TAKRI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
 12000;CUNEIFORM SIGN A;Lo;0;L;;;;;N;;;;;
 12001;CUNEIFORM SIGN A TIMES A;Lo;0;L;;;;;N;;;;;
 12002;CUNEIFORM SIGN A TIMES BAD;Lo;0;L;;;;;N;;;;;
@@ -19960,6 +20396,139 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
 16A36;BAMUM LETTER PHASE-F KPA;Lo;0;L;;;;;N;;;;;
 16A37;BAMUM LETTER PHASE-F SAMBA;Lo;0;L;;;;;N;;;;;
 16A38;BAMUM LETTER PHASE-F VUEQ;Lo;0;L;;;;;N;;;;;
+16F00;MIAO LETTER PA;Lo;0;L;;;;;N;;;;;
+16F01;MIAO LETTER BA;Lo;0;L;;;;;N;;;;;
+16F02;MIAO LETTER YI PA;Lo;0;L;;;;;N;;;;;
+16F03;MIAO LETTER PLA;Lo;0;L;;;;;N;;;;;
+16F04;MIAO LETTER MA;Lo;0;L;;;;;N;;;;;
+16F05;MIAO LETTER MHA;Lo;0;L;;;;;N;;;;;
+16F06;MIAO LETTER ARCHAIC MA;Lo;0;L;;;;;N;;;;;
+16F07;MIAO LETTER FA;Lo;0;L;;;;;N;;;;;
+16F08;MIAO LETTER VA;Lo;0;L;;;;;N;;;;;
+16F09;MIAO LETTER VFA;Lo;0;L;;;;;N;;;;;
+16F0A;MIAO LETTER TA;Lo;0;L;;;;;N;;;;;
+16F0B;MIAO LETTER DA;Lo;0;L;;;;;N;;;;;
+16F0C;MIAO LETTER YI TTA;Lo;0;L;;;;;N;;;;;
+16F0D;MIAO LETTER YI TA;Lo;0;L;;;;;N;;;;;
+16F0E;MIAO LETTER TTA;Lo;0;L;;;;;N;;;;;
+16F0F;MIAO LETTER DDA;Lo;0;L;;;;;N;;;;;
+16F10;MIAO LETTER NA;Lo;0;L;;;;;N;;;;;
+16F11;MIAO LETTER NHA;Lo;0;L;;;;;N;;;;;
+16F12;MIAO LETTER YI NNA;Lo;0;L;;;;;N;;;;;
+16F13;MIAO LETTER ARCHAIC NA;Lo;0;L;;;;;N;;;;;
+16F14;MIAO LETTER NNA;Lo;0;L;;;;;N;;;;;
+16F15;MIAO LETTER NNHA;Lo;0;L;;;;;N;;;;;
+16F16;MIAO LETTER LA;Lo;0;L;;;;;N;;;;;
+16F17;MIAO LETTER LYA;Lo;0;L;;;;;N;;;;;
+16F18;MIAO LETTER LHA;Lo;0;L;;;;;N;;;;;
+16F19;MIAO LETTER LHYA;Lo;0;L;;;;;N;;;;;
+16F1A;MIAO LETTER TLHA;Lo;0;L;;;;;N;;;;;
+16F1B;MIAO LETTER DLHA;Lo;0;L;;;;;N;;;;;
+16F1C;MIAO LETTER TLHYA;Lo;0;L;;;;;N;;;;;
+16F1D;MIAO LETTER DLHYA;Lo;0;L;;;;;N;;;;;
+16F1E;MIAO LETTER KA;Lo;0;L;;;;;N;;;;;
+16F1F;MIAO LETTER GA;Lo;0;L;;;;;N;;;;;
+16F20;MIAO LETTER YI KA;Lo;0;L;;;;;N;;;;;
+16F21;MIAO LETTER QA;Lo;0;L;;;;;N;;;;;
+16F22;MIAO LETTER QGA;Lo;0;L;;;;;N;;;;;
+16F23;MIAO LETTER NGA;Lo;0;L;;;;;N;;;;;
+16F24;MIAO LETTER NGHA;Lo;0;L;;;;;N;;;;;
+16F25;MIAO LETTER ARCHAIC NGA;Lo;0;L;;;;;N;;;;;
+16F26;MIAO LETTER HA;Lo;0;L;;;;;N;;;;;
+16F27;MIAO LETTER XA;Lo;0;L;;;;;N;;;;;
+16F28;MIAO LETTER GHA;Lo;0;L;;;;;N;;;;;
+16F29;MIAO LETTER GHHA;Lo;0;L;;;;;N;;;;;
+16F2A;MIAO LETTER TSSA;Lo;0;L;;;;;N;;;;;
+16F2B;MIAO LETTER DZZA;Lo;0;L;;;;;N;;;;;
+16F2C;MIAO LETTER NYA;Lo;0;L;;;;;N;;;;;
+16F2D;MIAO LETTER NYHA;Lo;0;L;;;;;N;;;;;
+16F2E;MIAO LETTER TSHA;Lo;0;L;;;;;N;;;;;
+16F2F;MIAO LETTER DZHA;Lo;0;L;;;;;N;;;;;
+16F30;MIAO LETTER YI TSHA;Lo;0;L;;;;;N;;;;;
+16F31;MIAO LETTER YI DZHA;Lo;0;L;;;;;N;;;;;
+16F32;MIAO LETTER REFORMED TSHA;Lo;0;L;;;;;N;;;;;
+16F33;MIAO LETTER SHA;Lo;0;L;;;;;N;;;;;
+16F34;MIAO LETTER SSA;Lo;0;L;;;;;N;;;;;
+16F35;MIAO LETTER ZHA;Lo;0;L;;;;;N;;;;;
+16F36;MIAO LETTER ZSHA;Lo;0;L;;;;;N;;;;;
+16F37;MIAO LETTER TSA;Lo;0;L;;;;;N;;;;;
+16F38;MIAO LETTER DZA;Lo;0;L;;;;;N;;;;;
+16F39;MIAO LETTER YI TSA;Lo;0;L;;;;;N;;;;;
+16F3A;MIAO LETTER SA;Lo;0;L;;;;;N;;;;;
+16F3B;MIAO LETTER ZA;Lo;0;L;;;;;N;;;;;
+16F3C;MIAO LETTER ZSA;Lo;0;L;;;;;N;;;;;
+16F3D;MIAO LETTER ZZA;Lo;0;L;;;;;N;;;;;
+16F3E;MIAO LETTER ZZSA;Lo;0;L;;;;;N;;;;;
+16F3F;MIAO LETTER ARCHAIC ZZA;Lo;0;L;;;;;N;;;;;
+16F40;MIAO LETTER ZZYA;Lo;0;L;;;;;N;;;;;
+16F41;MIAO LETTER ZZSYA;Lo;0;L;;;;;N;;;;;
+16F42;MIAO LETTER WA;Lo;0;L;;;;;N;;;;;
+16F43;MIAO LETTER AH;Lo;0;L;;;;;N;;;;;
+16F44;MIAO LETTER HHA;Lo;0;L;;;;;N;;;;;
+16F50;MIAO LETTER NASALIZATION;Lo;0;L;;;;;N;;;;;
+16F51;MIAO SIGN ASPIRATION;Mc;0;L;;;;;N;;;;;
+16F52;MIAO SIGN REFORMED VOICING;Mc;0;L;;;;;N;;;;;
+16F53;MIAO SIGN REFORMED ASPIRATION;Mc;0;L;;;;;N;;;;;
+16F54;MIAO VOWEL SIGN A;Mc;0;L;;;;;N;;;;;
+16F55;MIAO VOWEL SIGN AA;Mc;0;L;;;;;N;;;;;
+16F56;MIAO VOWEL SIGN AHH;Mc;0;L;;;;;N;;;;;
+16F57;MIAO VOWEL SIGN AN;Mc;0;L;;;;;N;;;;;
+16F58;MIAO VOWEL SIGN ANG;Mc;0;L;;;;;N;;;;;
+16F59;MIAO VOWEL SIGN O;Mc;0;L;;;;;N;;;;;
+16F5A;MIAO VOWEL SIGN OO;Mc;0;L;;;;;N;;;;;
+16F5B;MIAO VOWEL SIGN WO;Mc;0;L;;;;;N;;;;;
+16F5C;MIAO VOWEL SIGN W;Mc;0;L;;;;;N;;;;;
+16F5D;MIAO VOWEL SIGN E;Mc;0;L;;;;;N;;;;;
+16F5E;MIAO VOWEL SIGN EN;Mc;0;L;;;;;N;;;;;
+16F5F;MIAO VOWEL SIGN ENG;Mc;0;L;;;;;N;;;;;
+16F60;MIAO VOWEL SIGN OEY;Mc;0;L;;;;;N;;;;;
+16F61;MIAO VOWEL SIGN I;Mc;0;L;;;;;N;;;;;
+16F62;MIAO VOWEL SIGN IA;Mc;0;L;;;;;N;;;;;
+16F63;MIAO VOWEL SIGN IAN;Mc;0;L;;;;;N;;;;;
+16F64;MIAO VOWEL SIGN IANG;Mc;0;L;;;;;N;;;;;
+16F65;MIAO VOWEL SIGN IO;Mc;0;L;;;;;N;;;;;
+16F66;MIAO VOWEL SIGN IE;Mc;0;L;;;;;N;;;;;
+16F67;MIAO VOWEL SIGN II;Mc;0;L;;;;;N;;;;;
+16F68;MIAO VOWEL SIGN IU;Mc;0;L;;;;;N;;;;;
+16F69;MIAO VOWEL SIGN ING;Mc;0;L;;;;;N;;;;;
+16F6A;MIAO VOWEL SIGN U;Mc;0;L;;;;;N;;;;;
+16F6B;MIAO VOWEL SIGN UA;Mc;0;L;;;;;N;;;;;
+16F6C;MIAO VOWEL SIGN UAN;Mc;0;L;;;;;N;;;;;
+16F6D;MIAO VOWEL SIGN UANG;Mc;0;L;;;;;N;;;;;
+16F6E;MIAO VOWEL SIGN UU;Mc;0;L;;;;;N;;;;;
+16F6F;MIAO VOWEL SIGN UEI;Mc;0;L;;;;;N;;;;;
+16F70;MIAO VOWEL SIGN UNG;Mc;0;L;;;;;N;;;;;
+16F71;MIAO VOWEL SIGN Y;Mc;0;L;;;;;N;;;;;
+16F72;MIAO VOWEL SIGN YI;Mc;0;L;;;;;N;;;;;
+16F73;MIAO VOWEL SIGN AE;Mc;0;L;;;;;N;;;;;
+16F74;MIAO VOWEL SIGN AEE;Mc;0;L;;;;;N;;;;;
+16F75;MIAO VOWEL SIGN ERR;Mc;0;L;;;;;N;;;;;
+16F76;MIAO VOWEL SIGN ROUNDED ERR;Mc;0;L;;;;;N;;;;;
+16F77;MIAO VOWEL SIGN ER;Mc;0;L;;;;;N;;;;;
+16F78;MIAO VOWEL SIGN ROUNDED ER;Mc;0;L;;;;;N;;;;;
+16F79;MIAO VOWEL SIGN AI;Mc;0;L;;;;;N;;;;;
+16F7A;MIAO VOWEL SIGN EI;Mc;0;L;;;;;N;;;;;
+16F7B;MIAO VOWEL SIGN AU;Mc;0;L;;;;;N;;;;;
+16F7C;MIAO VOWEL SIGN OU;Mc;0;L;;;;;N;;;;;
+16F7D;MIAO VOWEL SIGN N;Mc;0;L;;;;;N;;;;;
+16F7E;MIAO VOWEL SIGN NG;Mc;0;L;;;;;N;;;;;
+16F8F;MIAO TONE RIGHT;Mn;0;NSM;;;;;N;;;;;
+16F90;MIAO TONE TOP RIGHT;Mn;0;NSM;;;;;N;;;;;
+16F91;MIAO TONE ABOVE;Mn;0;NSM;;;;;N;;;;;
+16F92;MIAO TONE BELOW;Mn;0;NSM;;;;;N;;;;;
+16F93;MIAO LETTER TONE-2;Lm;0;L;;;;;N;;;;;
+16F94;MIAO LETTER TONE-3;Lm;0;L;;;;;N;;;;;
+16F95;MIAO LETTER TONE-4;Lm;0;L;;;;;N;;;;;
+16F96;MIAO LETTER TONE-5;Lm;0;L;;;;;N;;;;;
+16F97;MIAO LETTER TONE-6;Lm;0;L;;;;;N;;;;;
+16F98;MIAO LETTER TONE-7;Lm;0;L;;;;;N;;;;;
+16F99;MIAO LETTER TONE-8;Lm;0;L;;;;;N;;;;;
+16F9A;MIAO LETTER REFORMED TONE-1;Lm;0;L;;;;;N;;;;;
+16F9B;MIAO LETTER REFORMED TONE-2;Lm;0;L;;;;;N;;;;;
+16F9C;MIAO LETTER REFORMED TONE-4;Lm;0;L;;;;;N;;;;;
+16F9D;MIAO LETTER REFORMED TONE-5;Lm;0;L;;;;;N;;;;;
+16F9E;MIAO LETTER REFORMED TONE-6;Lm;0;L;;;;;N;;;;;
+16F9F;MIAO LETTER REFORMED TONE-8;Lm;0;L;;;;;N;;;;;
 1B000;KATAKANA LETTER ARCHAIC E;Lo;0;L;;;;;N;;;;;
 1B001;HIRAGANA LETTER ARCHAIC YE;Lo;0;L;;;;;N;;;;;
 1D000;BYZANTINE MUSICAL SYMBOL PSILI;So;0;L;;;;;N;;;;;
@@ -21599,6 +22168,149 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
 1D7FD;MATHEMATICAL MONOSPACE DIGIT SEVEN;Nd;0;EN;<font> 0037;7;7;7;N;;;;;
 1D7FE;MATHEMATICAL MONOSPACE DIGIT EIGHT;Nd;0;EN;<font> 0038;8;8;8;N;;;;;
 1D7FF;MATHEMATICAL MONOSPACE DIGIT NINE;Nd;0;EN;<font> 0039;9;9;9;N;;;;;
+1EE00;ARABIC MATHEMATICAL ALEF;Lo;0;AL;<font> 0627;;;;N;;;;;
+1EE01;ARABIC MATHEMATICAL BEH;Lo;0;AL;<font> 0628;;;;N;;;;;
+1EE02;ARABIC MATHEMATICAL JEEM;Lo;0;AL;<font> 062C;;;;N;;;;;
+1EE03;ARABIC MATHEMATICAL DAL;Lo;0;AL;<font> 062F;;;;N;;;;;
+1EE05;ARABIC MATHEMATICAL WAW;Lo;0;AL;<font> 0648;;;;N;;;;;
+1EE06;ARABIC MATHEMATICAL ZAIN;Lo;0;AL;<font> 0632;;;;N;;;;;
+1EE07;ARABIC MATHEMATICAL HAH;Lo;0;AL;<font> 062D;;;;N;;;;;
+1EE08;ARABIC MATHEMATICAL TAH;Lo;0;AL;<font> 0637;;;;N;;;;;
+1EE09;ARABIC MATHEMATICAL YEH;Lo;0;AL;<font> 064A;;;;N;;;;;
+1EE0A;ARABIC MATHEMATICAL KAF;Lo;0;AL;<font> 0643;;;;N;;;;;
+1EE0B;ARABIC MATHEMATICAL LAM;Lo;0;AL;<font> 0644;;;;N;;;;;
+1EE0C;ARABIC MATHEMATICAL MEEM;Lo;0;AL;<font> 0645;;;;N;;;;;
+1EE0D;ARABIC MATHEMATICAL NOON;Lo;0;AL;<font> 0646;;;;N;;;;;
+1EE0E;ARABIC MATHEMATICAL SEEN;Lo;0;AL;<font> 0633;;;;N;;;;;
+1EE0F;ARABIC MATHEMATICAL AIN;Lo;0;AL;<font> 0639;;;;N;;;;;
+1EE10;ARABIC MATHEMATICAL FEH;Lo;0;AL;<font> 0641;;;;N;;;;;
+1EE11;ARABIC MATHEMATICAL SAD;Lo;0;AL;<font> 0635;;;;N;;;;;
+1EE12;ARABIC MATHEMATICAL QAF;Lo;0;AL;<font> 0642;;;;N;;;;;
+1EE13;ARABIC MATHEMATICAL REH;Lo;0;AL;<font> 0631;;;;N;;;;;
+1EE14;ARABIC MATHEMATICAL SHEEN;Lo;0;AL;<font> 0634;;;;N;;;;;
+1EE15;ARABIC MATHEMATICAL TEH;Lo;0;AL;<font> 062A;;;;N;;;;;
+1EE16;ARABIC MATHEMATICAL THEH;Lo;0;AL;<font> 062B;;;;N;;;;;
+1EE17;ARABIC MATHEMATICAL KHAH;Lo;0;AL;<font> 062E;;;;N;;;;;
+1EE18;ARABIC MATHEMATICAL THAL;Lo;0;AL;<font> 0630;;;;N;;;;;
+1EE19;ARABIC MATHEMATICAL DAD;Lo;0;AL;<font> 0636;;;;N;;;;;
+1EE1A;ARABIC MATHEMATICAL ZAH;Lo;0;AL;<font> 0638;;;;N;;;;;
+1EE1B;ARABIC MATHEMATICAL GHAIN;Lo;0;AL;<font> 063A;;;;N;;;;;
+1EE1C;ARABIC MATHEMATICAL DOTLESS BEH;Lo;0;AL;<font> 066E;;;;N;;;;;
+1EE1D;ARABIC MATHEMATICAL DOTLESS NOON;Lo;0;AL;<font> 06BA;;;;N;;;;;
+1EE1E;ARABIC MATHEMATICAL DOTLESS FEH;Lo;0;AL;<font> 06A1;;;;N;;;;;
+1EE1F;ARABIC MATHEMATICAL DOTLESS QAF;Lo;0;AL;<font> 066F;;;;N;;;;;
+1EE21;ARABIC MATHEMATICAL INITIAL BEH;Lo;0;AL;<font> 0628;;;;N;;;;;
+1EE22;ARABIC MATHEMATICAL INITIAL JEEM;Lo;0;AL;<font> 062C;;;;N;;;;;
+1EE24;ARABIC MATHEMATICAL INITIAL HEH;Lo;0;AL;<font> 0647;;;;N;;;;;
+1EE27;ARABIC MATHEMATICAL INITIAL HAH;Lo;0;AL;<font> 062D;;;;N;;;;;
+1EE29;ARABIC MATHEMATICAL INITIAL YEH;Lo;0;AL;<font> 064A;;;;N;;;;;
+1EE2A;ARABIC MATHEMATICAL INITIAL KAF;Lo;0;AL;<font> 0643;;;;N;;;;;
+1EE2B;ARABIC MATHEMATICAL INITIAL LAM;Lo;0;AL;<font> 0644;;;;N;;;;;
+1EE2C;ARABIC MATHEMATICAL INITIAL MEEM;Lo;0;AL;<font> 0645;;;;N;;;;;
+1EE2D;ARABIC MATHEMATICAL INITIAL NOON;Lo;0;AL;<font> 0646;;;;N;;;;;
+1EE2E;ARABIC MATHEMATICAL INITIAL SEEN;Lo;0;AL;<font> 0633;;;;N;;;;;
+1EE2F;ARABIC MATHEMATICAL INITIAL AIN;Lo;0;AL;<font> 0639;;;;N;;;;;
+1EE30;ARABIC MATHEMATICAL INITIAL FEH;Lo;0;AL;<font> 0641;;;;N;;;;;
+1EE31;ARABIC MATHEMATICAL INITIAL SAD;Lo;0;AL;<font> 0635;;;;N;;;;;
+1EE32;ARABIC MATHEMATICAL INITIAL QAF;Lo;0;AL;<font> 0642;;;;N;;;;;
+1EE34;ARABIC MATHEMATICAL INITIAL SHEEN;Lo;0;AL;<font> 0634;;;;N;;;;;
+1EE35;ARABIC MATHEMATICAL INITIAL TEH;Lo;0;AL;<font> 062A;;;;N;;;;;
+1EE36;ARABIC MATHEMATICAL INITIAL THEH;Lo;0;AL;<font> 062B;;;;N;;;;;
+1EE37;ARABIC MATHEMATICAL INITIAL KHAH;Lo;0;AL;<font> 062E;;;;N;;;;;
+1EE39;ARABIC MATHEMATICAL INITIAL DAD;Lo;0;AL;<font> 0636;;;;N;;;;;
+1EE3B;ARABIC MATHEMATICAL INITIAL GHAIN;Lo;0;AL;<font> 063A;;;;N;;;;;
+1EE42;ARABIC MATHEMATICAL TAILED JEEM;Lo;0;AL;<font> 062C;;;;N;;;;;
+1EE47;ARABIC MATHEMATICAL TAILED HAH;Lo;0;AL;<font> 062D;;;;N;;;;;
+1EE49;ARABIC MATHEMATICAL TAILED YEH;Lo;0;AL;<font> 064A;;;;N;;;;;
+1EE4B;ARABIC MATHEMATICAL TAILED LAM;Lo;0;AL;<font> 0644;;;;N;;;;;
+1EE4D;ARABIC MATHEMATICAL TAILED NOON;Lo;0;AL;<font> 0646;;;;N;;;;;
+1EE4E;ARABIC MATHEMATICAL TAILED SEEN;Lo;0;AL;<font> 0633;;;;N;;;;;
+1EE4F;ARABIC MATHEMATICAL TAILED AIN;Lo;0;AL;<font> 0639;;;;N;;;;;
+1EE51;ARABIC MATHEMATICAL TAILED SAD;Lo;0;AL;<font> 0635;;;;N;;;;;
+1EE52;ARABIC MATHEMATICAL TAILED QAF;Lo;0;AL;<font> 0642;;;;N;;;;;
+1EE54;ARABIC MATHEMATICAL TAILED SHEEN;Lo;0;AL;<font> 0634;;;;N;;;;;
+1EE57;ARABIC MATHEMATICAL TAILED KHAH;Lo;0;AL;<font> 062E;;;;N;;;;;
+1EE59;ARABIC MATHEMATICAL TAILED DAD;Lo;0;AL;<font> 0636;;;;N;;;;;
+1EE5B;ARABIC MATHEMATICAL TAILED GHAIN;Lo;0;AL;<font> 063A;;;;N;;;;;
+1EE5D;ARABIC MATHEMATICAL TAILED DOTLESS NOON;Lo;0;AL;<font> 06BA;;;;N;;;;;
+1EE5F;ARABIC MATHEMATICAL TAILED DOTLESS QAF;Lo;0;AL;<font> 066F;;;;N;;;;;
+1EE61;ARABIC MATHEMATICAL STRETCHED BEH;Lo;0;AL;<font> 0628;;;;N;;;;;
+1EE62;ARABIC MATHEMATICAL STRETCHED JEEM;Lo;0;AL;<font> 062C;;;;N;;;;;
+1EE64;ARABIC MATHEMATICAL STRETCHED HEH;Lo;0;AL;<font> 0647;;;;N;;;;;
+1EE67;ARABIC MATHEMATICAL STRETCHED HAH;Lo;0;AL;<font> 062D;;;;N;;;;;
+1EE68;ARABIC MATHEMATICAL STRETCHED TAH;Lo;0;AL;<font> 0637;;;;N;;;;;
+1EE69;ARABIC MATHEMATICAL STRETCHED YEH;Lo;0;AL;<font> 064A;;;;N;;;;;
+1EE6A;ARABIC MATHEMATICAL STRETCHED KAF;Lo;0;AL;<font> 0643;;;;N;;;;;
+1EE6C;ARABIC MATHEMATICAL STRETCHED MEEM;Lo;0;AL;<font> 0645;;;;N;;;;;
+1EE6D;ARABIC MATHEMATICAL STRETCHED NOON;Lo;0;AL;<font> 0646;;;;N;;;;;
+1EE6E;ARABIC MATHEMATICAL STRETCHED SEEN;Lo;0;AL;<font> 0633;;;;N;;;;;
+1EE6F;ARABIC MATHEMATICAL STRETCHED AIN;Lo;0;AL;<font> 0639;;;;N;;;;;
+1EE70;ARABIC MATHEMATICAL STRETCHED FEH;Lo;0;AL;<font> 0641;;;;N;;;;;
+1EE71;ARABIC MATHEMATICAL STRETCHED SAD;Lo;0;AL;<font> 0635;;;;N;;;;;
+1EE72;ARABIC MATHEMATICAL STRETCHED QAF;Lo;0;AL;<font> 0642;;;;N;;;;;
+1EE74;ARABIC MATHEMATICAL STRETCHED SHEEN;Lo;0;AL;<font> 0634;;;;N;;;;;
+1EE75;ARABIC MATHEMATICAL STRETCHED TEH;Lo;0;AL;<font> 062A;;;;N;;;;;
+1EE76;ARABIC MATHEMATICAL STRETCHED THEH;Lo;0;AL;<font> 062B;;;;N;;;;;
+1EE77;ARABIC MATHEMATICAL STRETCHED KHAH;Lo;0;AL;<font> 062E;;;;N;;;;;
+1EE79;ARABIC MATHEMATICAL STRETCHED DAD;Lo;0;AL;<font> 0636;;;;N;;;;;
+1EE7A;ARABIC MATHEMATICAL STRETCHED ZAH;Lo;0;AL;<font> 0638;;;;N;;;;;
+1EE7B;ARABIC MATHEMATICAL STRETCHED GHAIN;Lo;0;AL;<font> 063A;;;;N;;;;;
+1EE7C;ARABIC MATHEMATICAL STRETCHED DOTLESS BEH;Lo;0;AL;<font> 066E;;;;N;;;;;
+1EE7E;ARABIC MATHEMATICAL STRETCHED DOTLESS FEH;Lo;0;AL;<font> 06A1;;;;N;;;;;
+1EE80;ARABIC MATHEMATICAL LOOPED ALEF;Lo;0;AL;<font> 0627;;;;N;;;;;
+1EE81;ARABIC MATHEMATICAL LOOPED BEH;Lo;0;AL;<font> 0628;;;;N;;;;;
+1EE82;ARABIC MATHEMATICAL LOOPED JEEM;Lo;0;AL;<font> 062C;;;;N;;;;;
+1EE83;ARABIC MATHEMATICAL LOOPED DAL;Lo;0;AL;<font> 062F;;;;N;;;;;
+1EE84;ARABIC MATHEMATICAL LOOPED HEH;Lo;0;AL;<font> 0647;;;;N;;;;;
+1EE85;ARABIC MATHEMATICAL LOOPED WAW;Lo;0;AL;<font> 0648;;;;N;;;;;
+1EE86;ARABIC MATHEMATICAL LOOPED ZAIN;Lo;0;AL;<font> 0632;;;;N;;;;;
+1EE87;ARABIC MATHEMATICAL LOOPED HAH;Lo;0;AL;<font> 062D;;;;N;;;;;
+1EE88;ARABIC MATHEMATICAL LOOPED TAH;Lo;0;AL;<font> 0637;;;;N;;;;;
+1EE89;ARABIC MATHEMATICAL LOOPED YEH;Lo;0;AL;<font> 064A;;;;N;;;;;
+1EE8B;ARABIC MATHEMATICAL LOOPED LAM;Lo;0;AL;<font> 0644;;;;N;;;;;
+1EE8C;ARABIC MATHEMATICAL LOOPED MEEM;Lo;0;AL;<font> 0645;;;;N;;;;;
+1EE8D;ARABIC MATHEMATICAL LOOPED NOON;Lo;0;AL;<font> 0646;;;;N;;;;;
+1EE8E;ARABIC MATHEMATICAL LOOPED SEEN;Lo;0;AL;<font> 0633;;;;N;;;;;
+1EE8F;ARABIC MATHEMATICAL LOOPED AIN;Lo;0;AL;<font> 0639;;;;N;;;;;
+1EE90;ARABIC MATHEMATICAL LOOPED FEH;Lo;0;AL;<font> 0641;;;;N;;;;;
+1EE91;ARABIC MATHEMATICAL LOOPED SAD;Lo;0;AL;<font> 0635;;;;N;;;;;
+1EE92;ARABIC MATHEMATICAL LOOPED QAF;Lo;0;AL;<font> 0642;;;;N;;;;;
+1EE93;ARABIC MATHEMATICAL LOOPED REH;Lo;0;AL;<font> 0631;;;;N;;;;;
+1EE94;ARABIC MATHEMATICAL LOOPED SHEEN;Lo;0;AL;<font> 0634;;;;N;;;;;
+1EE95;ARABIC MATHEMATICAL LOOPED TEH;Lo;0;AL;<font> 062A;;;;N;;;;;
+1EE96;ARABIC MATHEMATICAL LOOPED THEH;Lo;0;AL;<font> 062B;;;;N;;;;;
+1EE97;ARABIC MATHEMATICAL LOOPED KHAH;Lo;0;AL;<font> 062E;;;;N;;;;;
+1EE98;ARABIC MATHEMATICAL LOOPED THAL;Lo;0;AL;<font> 0630;;;;N;;;;;
+1EE99;ARABIC MATHEMATICAL LOOPED DAD;Lo;0;AL;<font> 0636;;;;N;;;;;
+1EE9A;ARABIC MATHEMATICAL LOOPED ZAH;Lo;0;AL;<font> 0638;;;;N;;;;;
+1EE9B;ARABIC MATHEMATICAL LOOPED GHAIN;Lo;0;AL;<font> 063A;;;;N;;;;;
+1EEA1;ARABIC MATHEMATICAL DOUBLE-STRUCK BEH;Lo;0;AL;<font> 0628;;;;N;;;;;
+1EEA2;ARABIC MATHEMATICAL DOUBLE-STRUCK JEEM;Lo;0;AL;<font> 062C;;;;N;;;;;
+1EEA3;ARABIC MATHEMATICAL DOUBLE-STRUCK DAL;Lo;0;AL;<font> 062F;;;;N;;;;;
+1EEA5;ARABIC MATHEMATICAL DOUBLE-STRUCK WAW;Lo;0;AL;<font> 0648;;;;N;;;;;
+1EEA6;ARABIC MATHEMATICAL DOUBLE-STRUCK ZAIN;Lo;0;AL;<font> 0632;;;;N;;;;;
+1EEA7;ARABIC MATHEMATICAL DOUBLE-STRUCK HAH;Lo;0;AL;<font> 062D;;;;N;;;;;
+1EEA8;ARABIC MATHEMATICAL DOUBLE-STRUCK TAH;Lo;0;AL;<font> 0637;;;;N;;;;;
+1EEA9;ARABIC MATHEMATICAL DOUBLE-STRUCK YEH;Lo;0;AL;<font> 064A;;;;N;;;;;
+1EEAB;ARABIC MATHEMATICAL DOUBLE-STRUCK LAM;Lo;0;AL;<font> 0644;;;;N;;;;;
+1EEAC;ARABIC MATHEMATICAL DOUBLE-STRUCK MEEM;Lo;0;AL;<font> 0645;;;;N;;;;;
+1EEAD;ARABIC MATHEMATICAL DOUBLE-STRUCK NOON;Lo;0;AL;<font> 0646;;;;N;;;;;
+1EEAE;ARABIC MATHEMATICAL DOUBLE-STRUCK SEEN;Lo;0;AL;<font> 0633;;;;N;;;;;
+1EEAF;ARABIC MATHEMATICAL DOUBLE-STRUCK AIN;Lo;0;AL;<font> 0639;;;;N;;;;;
+1EEB0;ARABIC MATHEMATICAL DOUBLE-STRUCK FEH;Lo;0;AL;<font> 0641;;;;N;;;;;
+1EEB1;ARABIC MATHEMATICAL DOUBLE-STRUCK SAD;Lo;0;AL;<font> 0635;;;;N;;;;;
+1EEB2;ARABIC MATHEMATICAL DOUBLE-STRUCK QAF;Lo;0;AL;<font> 0642;;;;N;;;;;
+1EEB3;ARABIC MATHEMATICAL DOUBLE-STRUCK REH;Lo;0;AL;<font> 0631;;;;N;;;;;
+1EEB4;ARABIC MATHEMATICAL DOUBLE-STRUCK SHEEN;Lo;0;AL;<font> 0634;;;;N;;;;;
+1EEB5;ARABIC MATHEMATICAL DOUBLE-STRUCK TEH;Lo;0;AL;<font> 062A;;;;N;;;;;
+1EEB6;ARABIC MATHEMATICAL DOUBLE-STRUCK THEH;Lo;0;AL;<font> 062B;;;;N;;;;;
+1EEB7;ARABIC MATHEMATICAL DOUBLE-STRUCK KHAH;Lo;0;AL;<font> 062E;;;;N;;;;;
+1EEB8;ARABIC MATHEMATICAL DOUBLE-STRUCK THAL;Lo;0;AL;<font> 0630;;;;N;;;;;
+1EEB9;ARABIC MATHEMATICAL DOUBLE-STRUCK DAD;Lo;0;AL;<font> 0636;;;;N;;;;;
+1EEBA;ARABIC MATHEMATICAL DOUBLE-STRUCK ZAH;Lo;0;AL;<font> 0638;;;;N;;;;;
+1EEBB;ARABIC MATHEMATICAL DOUBLE-STRUCK GHAIN;Lo;0;AL;<font> 063A;;;;N;;;;;
+1EEF0;ARABIC MATHEMATICAL OPERATOR MEEM WITH HAH WITH TATWEEL;Sm;0;ON;;;;;N;;;;;
+1EEF1;ARABIC MATHEMATICAL OPERATOR HAH WITH DAL;Sm;0;ON;;;;;N;;;;;
 1F000;MAHJONG TILE EAST WIND;So;0;ON;;;;;N;;;;;
 1F001;MAHJONG TILE SOUTH WIND;So;0;ON;;;;;N;;;;;
 1F002;MAHJONG TILE WEST WIND;So;0;ON;;;;;N;;;;;
@@ -21902,6 +22614,8 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
 1F167;NEGATIVE CIRCLED LATIN CAPITAL LETTER X;So;0;L;;;;;N;;;;;
 1F168;NEGATIVE CIRCLED LATIN CAPITAL LETTER Y;So;0;L;;;;;N;;;;;
 1F169;NEGATIVE CIRCLED LATIN CAPITAL LETTER Z;So;0;L;;;;;N;;;;;
+1F16A;RAISED MC SIGN;So;0;ON;<super> 004D 0043;;;;N;;;;;
+1F16B;RAISED MD SIGN;So;0;ON;<super> 004D 0044;;;;N;;;;;
 1F170;NEGATIVE SQUARED LATIN CAPITAL LETTER A;So;0;L;;;;;N;;;;;
 1F171;NEGATIVE SQUARED LATIN CAPITAL LETTER B;So;0;L;;;;;N;;;;;
 1F172;NEGATIVE SQUARED LATIN CAPITAL LETTER C;So;0;L;;;;;N;;;;;
@@ -22354,7 +23068,7 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
 1F489;SYRINGE;So;0;ON;;;;;N;;;;;
 1F48A;PILL;So;0;ON;;;;;N;;;;;
 1F48B;KISS MARK;So;0;ON;;;;;N;;;;;
-1F48C;LOVE LETTER;So;0;L;;;;;N;;;;;
+1F48C;LOVE LETTER;So;0;ON;;;;;N;;;;;
 1F48D;RING;So;0;ON;;;;;N;;;;;
 1F48E;GEM STONE;So;0;ON;;;;;N;;;;;
 1F48F;KISS;So;0;ON;;;;;N;;;;;
@@ -22502,7 +23216,7 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
 1F521;INPUT SYMBOL FOR LATIN SMALL LETTERS;So;0;ON;;;;;N;;;;;
 1F522;INPUT SYMBOL FOR NUMBERS;So;0;ON;;;;;N;;;;;
 1F523;INPUT SYMBOL FOR SYMBOLS;So;0;ON;;;;;N;;;;;
-1F524;INPUT SYMBOL FOR LATIN LETTERS;So;0;L;;;;;N;;;;;
+1F524;INPUT SYMBOL FOR LATIN LETTERS;So;0;ON;;;;;N;;;;;
 1F525;FIRE;So;0;ON;;;;;N;;;;;
 1F526;ELECTRIC TORCH;So;0;ON;;;;;N;;;;;
 1F527;WRENCH;So;0;ON;;;;;N;;;;;
@@ -22528,6 +23242,10 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
 1F53B;DOWN-POINTING RED TRIANGLE;So;0;ON;;;;;N;;;;;
 1F53C;UP-POINTING SMALL RED TRIANGLE;So;0;ON;;;;;N;;;;;
 1F53D;DOWN-POINTING SMALL RED TRIANGLE;So;0;ON;;;;;N;;;;;
+1F540;CIRCLED CROSS POMMEE;So;0;ON;;;;;N;;;;;
+1F541;CROSS POMMEE WITH HALF-CIRCLE BELOW;So;0;ON;;;;;N;;;;;
+1F542;CROSS POMMEE;So;0;ON;;;;;N;;;;;
+1F543;NOTCHED LEFT SEMICIRCLE WITH THREE DOTS;So;0;ON;;;;;N;;;;;
 1F550;CLOCK FACE ONE OCLOCK;So;0;ON;;;;;N;;;;;
 1F551;CLOCK FACE TWO OCLOCK;So;0;ON;;;;;N;;;;;
 1F552;CLOCK FACE THREE OCLOCK;So;0;ON;;;;;N;;;;;
@@ -22557,6 +23275,7 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
 1F5FD;STATUE OF LIBERTY;So;0;ON;;;;;N;;;;;
 1F5FE;SILHOUETTE OF JAPAN;So;0;ON;;;;;N;;;;;
 1F5FF;MOYAI;So;0;ON;;;;;N;;;;;
+1F600;GRINNING FACE;So;0;ON;;;;;N;;;;;
 1F601;GRINNING FACE WITH SMILING EYES;So;0;ON;;;;;N;;;;;
 1F602;FACE WITH TEARS OF JOY;So;0;ON;;;;;N;;;;;
 1F603;SMILING FACE WITH OPEN MOUTH;So;0;ON;;;;;N;;;;;
@@ -22573,30 +23292,42 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
 1F60E;SMILING FACE WITH SUNGLASSES;So;0;ON;;;;;N;;;;;
 1F60F;SMIRKING FACE;So;0;ON;;;;;N;;;;;
 1F610;NEUTRAL FACE;So;0;ON;;;;;N;;;;;
+1F611;EXPRESSIONLESS FACE;So;0;ON;;;;;N;;;;;
 1F612;UNAMUSED FACE;So;0;ON;;;;;N;;;;;
 1F613;FACE WITH COLD SWEAT;So;0;ON;;;;;N;;;;;
 1F614;PENSIVE FACE;So;0;ON;;;;;N;;;;;
+1F615;CONFUSED FACE;So;0;ON;;;;;N;;;;;
 1F616;CONFOUNDED FACE;So;0;ON;;;;;N;;;;;
+1F617;KISSING FACE;So;0;ON;;;;;N;;;;;
 1F618;FACE THROWING A KISS;So;0;ON;;;;;N;;;;;
+1F619;KISSING FACE WITH SMILING EYES;So;0;ON;;;;;N;;;;;
 1F61A;KISSING FACE WITH CLOSED EYES;So;0;ON;;;;;N;;;;;
+1F61B;FACE WITH STUCK-OUT TONGUE;So;0;ON;;;;;N;;;;;
 1F61C;FACE WITH STUCK-OUT TONGUE AND WINKING EYE;So;0;ON;;;;;N;;;;;
 1F61D;FACE WITH STUCK-OUT TONGUE AND TIGHTLY-CLOSED EYES;So;0;ON;;;;;N;;;;;
 1F61E;DISAPPOINTED FACE;So;0;ON;;;;;N;;;;;
+1F61F;WORRIED FACE;So;0;ON;;;;;N;;;;;
 1F620;ANGRY FACE;So;0;ON;;;;;N;;;;;
 1F621;POUTING FACE;So;0;ON;;;;;N;;;;;
 1F622;CRYING FACE;So;0;ON;;;;;N;;;;;
 1F623;PERSEVERING FACE;So;0;ON;;;;;N;;;;;
 1F624;FACE WITH LOOK OF TRIUMPH;So;0;ON;;;;;N;;;;;
 1F625;DISAPPOINTED BUT RELIEVED FACE;So;0;ON;;;;;N;;;;;
+1F626;FROWNING FACE WITH OPEN MOUTH;So;0;ON;;;;;N;;;;;
+1F627;ANGUISHED FACE;So;0;ON;;;;;N;;;;;
 1F628;FEARFUL FACE;So;0;ON;;;;;N;;;;;
 1F629;WEARY FACE;So;0;ON;;;;;N;;;;;
 1F62A;SLEEPY FACE;So;0;ON;;;;;N;;;;;
 1F62B;TIRED FACE;So;0;ON;;;;;N;;;;;
+1F62C;GRIMACING FACE;So;0;ON;;;;;N;;;;;
 1F62D;LOUDLY CRYING FACE;So;0;ON;;;;;N;;;;;
+1F62E;FACE WITH OPEN MOUTH;So;0;ON;;;;;N;;;;;
+1F62F;HUSHED FACE;So;0;ON;;;;;N;;;;;
 1F630;FACE WITH OPEN MOUTH AND COLD SWEAT;So;0;ON;;;;;N;;;;;
 1F631;FACE SCREAMING IN FEAR;So;0;ON;;;;;N;;;;;
 1F632;ASTONISHED FACE;So;0;ON;;;;;N;;;;;
 1F633;FLUSHED FACE;So;0;ON;;;;;N;;;;;
+1F634;SLEEPING FACE;So;0;ON;;;;;N;;;;;
 1F635;DIZZY FACE;So;0;ON;;;;;N;;;;;
 1F636;FACE WITHOUT MOUTH;So;0;ON;;;;;N;;;;;
 1F637;FACE WITH MEDICAL MASK;So;0;ON;;;;;N;;;;;
diff --git a/js/xpconnect/src/nsXPConnect.cpp b/js/xpconnect/src/nsXPConnect.cpp
index 737de67173bf..5e3d629ce945 100644
--- a/js/xpconnect/src/nsXPConnect.cpp
+++ b/js/xpconnect/src/nsXPConnect.cpp
@@ -42,6 +42,10 @@
 
 /* High level class and public functions implementation. */
 
+#include "mozilla/Assertions.h"
+#include "mozilla/Base64.h"
+#include "mozilla/Util.h"
+
 #include "xpcprivate.h"
 #include "XPCWrapper.h"
 #include "nsBaseHashtable.h"
@@ -64,10 +68,6 @@
 #include "XPCQuickStubs.h"
 #include "dombindings.h"
 
-#include "mozilla/Assertions.h"
-#include "mozilla/Base64.h"
-#include "mozilla/Util.h"
-
 #include "nsWrapperCacheInlines.h"
 
 NS_IMPL_THREADSAFE_ISUPPORTS7(nsXPConnect,
@@ -166,7 +166,7 @@ nsXPConnect::GetXPConnect()
     // XPConnect off the main thread. If you're an extension developer hitting
     // this, you need to change your code. See bug 716167.
     if (!NS_LIKELY(NS_IsMainThread() || NS_IsCycleCollectorThread()))
-        JS_Assert("NS_IsMainThread()", __FILE__, __LINE__);
+        MOZ_Assert("NS_IsMainThread()", __FILE__, __LINE__);
 
     if (!gSelf) {
         if (gOnceAliveNowDead)
@@ -480,9 +480,10 @@ struct NoteWeakMapChildrenTracer : public JSTracer
 };
 
 static void
-TraceWeakMappingChild(JSTracer *trc, void *thing, JSGCTraceKind kind)
+TraceWeakMappingChild(JSTracer *trc, void **thingp, JSGCTraceKind kind)
 {
-    JS_ASSERT(trc->callback == TraceWeakMappingChild);
+    MOZ_ASSERT(trc->callback == TraceWeakMappingChild);
+    void *thing = *thingp;
     NoteWeakMapChildrenTracer *tracer =
         static_cast<NoteWeakMapChildrenTracer *>(trc);
     if (kind == JSTRACE_STRING)
@@ -513,7 +514,7 @@ TraceWeakMapping(js::WeakMapTracer *trc, JSObject *m,
                  void *k, JSGCTraceKind kkind,
                  void *v, JSGCTraceKind vkind)
 {
-    JS_ASSERT(trc->callback == TraceWeakMapping);
+    MOZ_ASSERT(trc->callback == TraceWeakMapping);
     NoteWeakMapsTracer *tracer = static_cast<NoteWeakMapsTracer *>(trc);
     if (vkind == JSTRACE_STRING)
         return;
@@ -524,7 +525,7 @@ TraceWeakMapping(js::WeakMapTracer *trc, JSObject *m,
     // reason about the liveness of their keys, which in turn requires that
     // the key can be represented in the cycle collector graph.  All existing
     // uses of weak maps use either objects or scripts as keys, which are okay.
-    JS_ASSERT(AddToCCKind(kkind));
+    MOZ_ASSERT(AddToCCKind(kkind));
 
     // As an emergency fallback for non-debug builds, if the key is not
     // representable in the cycle collector graph, we treat it as marked.  This
@@ -721,8 +722,9 @@ xpc_GCThingIsGrayCCThing(void *thing)
  * re-coloring.
  */
 static void
-UnmarkGrayChildren(JSTracer *trc, void *thing, JSGCTraceKind kind)
+UnmarkGrayChildren(JSTracer *trc, void **thingp, JSGCTraceKind kind)
 {
+    void *thing = *thingp;
     int stackDummy;
     if (!JS_CHECK_STACK_SIZE(js::GetContextStackLimit(trc->context), &stackDummy)) {
         /*
@@ -776,9 +778,10 @@ struct TraversalTracer : public JSTracer
 };
 
 static void
-NoteJSChild(JSTracer *trc, void *thing, JSGCTraceKind kind)
+NoteJSChild(JSTracer *trc, void **thingp, JSGCTraceKind kind)
 {
     TraversalTracer *tracer = static_cast<TraversalTracer*>(trc);
+    void *thing = *thingp;
 
     // Don't traverse non-gray objects, unless we want all traces.
     if (!xpc_IsGrayGCThing(thing) && !tracer->cb.WantAllTraces())
@@ -1010,7 +1013,7 @@ nsXPConnect::GetOutstandingRequests(JSContext* cx)
     // Ignore the contribution from the XPCCallContext we created for cycle
     // collection.
     if (context && cx == context->GetJSContext()) {
-        JS_ASSERT(n);
+        MOZ_ASSERT(n);
         --n;
     }
     return n;
@@ -1167,7 +1170,7 @@ struct VerifyTraceXPCGlobalCalledTracer
 };
 
 static void
-VerifyTraceXPCGlobalCalled(JSTracer *trc, void *thing, JSGCTraceKind kind)
+VerifyTraceXPCGlobalCalled(JSTracer *trc, void **thingp, JSGCTraceKind kind)
 {
     // We don't do anything here, we only want to verify that TraceXPCGlobal
     // was called.
@@ -1302,7 +1305,7 @@ nsXPConnect::InitClassesWithNewWrappedGlobal(JSContext * aJSContext,
 
     // voodoo to fixup scoping and parenting...
 
-    JS_ASSERT(!js::GetObjectParent(globalJSObj));
+    MOZ_ASSERT(!js::GetObjectParent(globalJSObj));
 
     JSObject* oldGlobal = JS_GetGlobalObject(aJSContext);
     if (!oldGlobal || oldGlobal == tempGlobal)
diff --git a/js/xpconnect/src/xpcprivate.h b/js/xpconnect/src/xpcprivate.h
index 404704255d39..3cb76b5f23f1 100644
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -46,6 +46,7 @@
 #ifndef xpcprivate_h___
 #define xpcprivate_h___
 
+#include "mozilla/Assertions.h"
 #include "mozilla/Attributes.h"
 
 #include <string.h>
@@ -1608,7 +1609,7 @@ public:
 
     static XPCWrappedNativeScope *GetNativeScope(JSContext *cx, JSObject *obj)
     {
-        JS_ASSERT(js::GetObjectClass(obj)->flags & JSCLASS_XPCONNECT_GLOBAL);
+        MOZ_ASSERT(js::GetObjectClass(obj)->flags & JSCLASS_XPCONNECT_GLOBAL);
 
         const js::Value &v = js::GetObjectSlot(obj, JSCLASS_GLOBAL_SLOT_COUNT);
         return v.isUndefined()
@@ -3020,7 +3021,10 @@ public:
     JSBool IsAggregatedToNative() const {return mRoot->mOuter != nsnull;}
     nsISupports* GetAggregatedNativeObject() const {return mRoot->mOuter;}
 
-    void SetIsMainThreadOnly() {JS_ASSERT(mMainThread); mMainThreadOnly = true;}
+    void SetIsMainThreadOnly() {
+        MOZ_ASSERT(mMainThread);
+        mMainThreadOnly = true;
+    }
     bool IsMainThreadOnly() const {return mMainThreadOnly;}
 
     void TraceJS(JSTracer* trc);
@@ -3649,7 +3653,7 @@ public:
         // XPConnect off the main thread. If you're an extension developer hitting
         // this, you need to change your code. See bug 716167.
         if (!NS_LIKELY(NS_IsMainThread() || NS_IsCycleCollectorThread()))
-            JS_Assert("NS_IsMainThread()", __FILE__, __LINE__);
+            MOZ_Assert("NS_IsMainThread()", __FILE__, __LINE__);
 
         if (cx) {
             if (js::GetOwnerThread(cx) == sMainJSThread)
diff --git a/layout/base/FrameLayerBuilder.cpp b/layout/base/FrameLayerBuilder.cpp
index d68b168c60b0..bb5f594de451 100644
--- a/layout/base/FrameLayerBuilder.cpp
+++ b/layout/base/FrameLayerBuilder.cpp
@@ -989,7 +989,8 @@ ContainerState::PopThebesLayerData()
   nsRefPtr<Layer> layer;
   nsRefPtr<ImageContainer> imageContainer = data->CanOptimizeImageLayer(); 
 
-  if (data->mIsSolidColorInVisibleRegion || imageContainer) {
+  if ((data->mIsSolidColorInVisibleRegion || imageContainer) &&
+      data->mLayer->GetValidRegion().IsEmpty()) {
     NS_ASSERTION(!(data->mIsSolidColorInVisibleRegion && imageContainer),
                  "Can't be a solid color as well as an image!");
     if (imageContainer) {
diff --git a/layout/base/tests/bug583889_inner1.html b/layout/base/tests/bug583889_inner1.html
index abf4a9d6fcef..7a9cddddf39e 100644
--- a/layout/base/tests/bug583889_inner1.html
+++ b/layout/base/tests/bug583889_inner1.html
@@ -20,6 +20,7 @@ function postPos() {
 function runTest() {
   var inner = document.getElementById("inner");
   window.onload = grabEventAndGo;
+  // Wait for onLoad event.
   yield;
 
   document.body.scrollTop = 300;
@@ -29,6 +30,8 @@ function runTest() {
 
   inner.src = "bug583889_inner2.html#id1";
   inner.onload = grabEventAndGo;
+  // Let parent process sent message.
+  // Wait for onLoad event from 'inner' iframe.
   yield;
   
   postPos();
@@ -37,17 +40,24 @@ function runTest() {
   dump("hi");
   inner.contentWindow.location = "bug583889_inner2.html#id2"
   waitAsync();
+  // Let parent process sent message.
+  // Let 'inner' iframe update itself.
   yield;
 
   postPos();
 
   inner.contentWindow.location.hash = "#id3"
   waitAsync();
+  // Let parent process sent message.
+  // Let 'inner' iframe update itself.
   yield;
 
   postPos();
 
   parent.postMessage("done", "*");
+  // Let parent process sent messages.
+  // "End" generator.
+  yield;
 }
 
 var gen = runTest();
diff --git a/layout/base/tests/test_bug583889.html b/layout/base/tests/test_bug583889.html
index edafa33eb631..163c0f1dbb9f 100644
--- a/layout/base/tests/test_bug583889.html
+++ b/layout/base/tests/test_bug583889.html
@@ -23,11 +23,13 @@ function grabEventAndGo(event) {
 
 function runTest() {
   window.onload = grabEventAndGo;
+  // Wait for onLoad event.
   yield;
 
   var inner = $("inner");
   inner.src = "bug583889_inner1.html";
   window.onmessage = grabEventAndGo;
+  // Wait for message from 'inner' iframe.
   event = yield;
 
   while (event.data != "done") {
@@ -35,10 +37,13 @@ function runTest() {
     is(data.top, 300, "should remain at same top");
     is(data.left, 300, "should remain at same left");
 
+    // Wait for message from 'inner' iframe.
     event = yield;
   }
 
-  SimpleTest.finish();
+  // finish(), yet let the test actually end first, to be safe.
+  SimpleTest.executeSoon(SimpleTest.finish);
+  // "End" generator.
   yield;
 }
 
diff --git a/layout/build/Makefile.in b/layout/build/Makefile.in
index 4c42e8bd4d40..0f899c5dfac2 100644
--- a/layout/build/Makefile.in
+++ b/layout/build/Makefile.in
@@ -148,6 +148,10 @@ LOCAL_INCLUDES	+= \
 	$(NULL)
 endif
 
+ifdef MOZ_B2G_BT #{
+SHARED_LIBRARY_LIBS	+= $(DEPTH)/dom/bluetooth/$(LIB_PREFIX)dombluetooth_s.$(LIB_SUFFIX)
+endif #}
+
 ifdef MOZ_B2G_RIL #{
 SHARED_LIBRARY_LIBS	+= $(DEPTH)/dom/system/b2g/$(LIB_PREFIX)domsystemb2g_s.$(LIB_SUFFIX)
 endif #}
@@ -272,5 +276,8 @@ ifdef MOZ_B2G_RIL #{
 LOCAL_INCLUDES	+= -I$(topsrcdir)/dom/system/b2g
 endif #}
 
+ifdef MOZ_B2G_BT #{
+LOCAL_INCLUDES	+= -I$(topsrcdir)/dom/bluetooth
+endif #}
 
 DEFINES += -D_IMPL_NS_LAYOUT
diff --git a/layout/reftests/bugs/reftest.list b/layout/reftests/bugs/reftest.list
index 92886094b786..b12f6e32f372 100644
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -1575,7 +1575,7 @@ asserts(0-11) == 582146-1.html about:blank
 == 584699-1.html 584699-1-ref.html
 == 585598-2.xhtml 585598-2-ref.xhtml
 == 586400-1.html 586400-1-ref.html
-fails-if(Android) fails-if(cocoaWidget) == 586683-1.html 586683-1-ref.html
+fuzzy-if(d2d,1,1051) fails-if(Android) fails-if(cocoaWidget) == 586683-1.html 586683-1-ref.html
 == 589615-1a.xhtml 589615-1-ref.html
 == 589615-1b.html 589615-1-ref.html
 == 589672-1.html 589672-1-ref.html
@@ -1690,6 +1690,6 @@ needs-focus != 703186-1.html 703186-2.html
 == 714519-1-q.html 714519-1-ref.html
 == 714519-2-as.html 714519-2-ref.html
 == 714519-2-q.html 714519-2-ref.html
-fuzzy-if(cocoaWidget,1,170) == 718521.html 718521-ref.html
+fuzzy-if(d2d,1,19) fuzzy-if(cocoaWidget,1,170) == 718521.html 718521-ref.html
 == 720987.html 720987-ref.html
 == 722923-1.html 722923-1-ref.html
diff --git a/layout/style/nsCSSParser.cpp b/layout/style/nsCSSParser.cpp
index e774246eb3fc..16e9c6b14632 100644
--- a/layout/style/nsCSSParser.cpp
+++ b/layout/style/nsCSSParser.cpp
@@ -4331,7 +4331,7 @@ CSSParserImpl::TranslateDimension(nsCSSValue& aValue,
     // Must be a zero number...
     NS_ASSERTION(0 == aNumber, "numbers without units must be 0");
     if ((VARIANT_LENGTH & aVariantMask) != 0) {
-      units = eCSSUnit_Point;
+      units = eCSSUnit_Pixel;
       type = VARIANT_LENGTH;
     }
     else if ((VARIANT_ANGLE & aVariantMask) != 0) {
@@ -7765,6 +7765,35 @@ CSSParserImpl::ParseSingleTransform(nsCSSValue& aValue, bool& aIs3D)
                                    minElems, maxElems, variantMask, aIs3D))
     return false;
 
+  // Bug 721136: Normalize the identifier to lowercase, except that things
+  // like scaleX should have the last character capitalized.  This matches
+  // what other browsers do.
+  nsContentUtils::ASCIIToLower(mToken.mIdent);
+  switch (keyword) {
+    case eCSSKeyword_rotatex:
+    case eCSSKeyword_scalex:
+    case eCSSKeyword_skewx:
+    case eCSSKeyword_translatex:
+      mToken.mIdent.Replace(mToken.mIdent.Length() - 1, 1, PRUnichar('X'));
+      break;
+
+    case eCSSKeyword_rotatey:
+    case eCSSKeyword_scaley:
+    case eCSSKeyword_skewy:
+    case eCSSKeyword_translatey:
+      mToken.mIdent.Replace(mToken.mIdent.Length() - 1, 1, PRUnichar('Y'));
+      break;
+
+    case eCSSKeyword_rotatez:
+    case eCSSKeyword_scalez:
+    case eCSSKeyword_translatez:
+      mToken.mIdent.Replace(mToken.mIdent.Length() - 1, 1, PRUnichar('Z'));
+      break;
+
+    default:
+      break;
+  }
+
   return ParseFunction(mToken.mIdent, variantMask, minElems, maxElems, aValue);
 }
 
diff --git a/layout/style/nsComputedDOMStyle.cpp b/layout/style/nsComputedDOMStyle.cpp
index 5a0f3988447d..fd593bd416f6 100644
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -83,6 +83,7 @@
 #include "nsDisplayList.h"
 #include "nsDOMCSSDeclaration.h"
 #include "mozilla/dom/Element.h"
+#include "nsGenericElement.h"
 #include "CSSCalc.h"
 
 using namespace mozilla;
@@ -161,8 +162,26 @@ nsComputedDOMStyle::Shutdown()
 }
 
 
+// If nsComputedDOMStyle is changed so that any additional fields are
+// traversed by the cycle collector (for instance, if wrapper cache
+// handling is changed) then CAN_SKIP must be updated.
 NS_IMPL_CYCLE_COLLECTION_1(nsComputedDOMStyle, mContent)
 
+// nsComputedDOMStyle has only one cycle collected field, so if
+// mContent is going to be skipped, the style isn't part of a garbage
+// cycle.
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsComputedDOMStyle)
+  return !tmp->mContent || nsGenericElement::CanSkip(tmp->mContent, true);
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
+
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsComputedDOMStyle)
+  return !tmp->mContent || nsGenericElement::CanSkipInCC(tmp->mContent);
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
+
+// CanSkipThis returns false to avoid problems with incomplete unlinking.
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsComputedDOMStyle)
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
+
 // QueryInterface implementation for nsComputedDOMStyle
 NS_INTERFACE_MAP_BEGIN(nsComputedDOMStyle)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
diff --git a/layout/style/nsComputedDOMStyle.h b/layout/style/nsComputedDOMStyle.h
index a5637befb2bb..cd38c9997f3b 100644
--- a/layout/style/nsComputedDOMStyle.h
+++ b/layout/style/nsComputedDOMStyle.h
@@ -62,8 +62,8 @@ class nsComputedDOMStyle : public nsDOMCSSDeclaration,
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-  NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsComputedDOMStyle,
-                                           nsICSSDeclaration)
+  NS_DECL_CYCLE_COLLECTION_SKIPPABLE_CLASS_AMBIGUOUS(nsComputedDOMStyle,
+                                                     nsICSSDeclaration)
 
   NS_IMETHOD Init(nsIDOMElement *aElement,
                   const nsAString& aPseudoElt,
diff --git a/layout/style/test/Makefile.in b/layout/style/test/Makefile.in
index 41dd9cedf3e5..86d9b1321649 100644
--- a/layout/style/test/Makefile.in
+++ b/layout/style/test/Makefile.in
@@ -229,6 +229,7 @@ _TEST_FILES =	test_acid3_test46.html \
 		visited_image_loading_frame.html \
 		visited_image_loading_frame_empty.html \
 		test_load_events_on_stylesheets.html \
+		test_bug721136.html \
 		$(NULL)
 
 _VISITED_REFTEST_FILES = \
diff --git a/layout/style/test/test_bug721136.html b/layout/style/test/test_bug721136.html
new file mode 100644
index 000000000000..1dec8e8d24b8
--- /dev/null
+++ b/layout/style/test/test_bug721136.html
@@ -0,0 +1,45 @@
+<!doctype html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=721136
+-->
+<title>Test for Bug 721136</title>
+<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=721136">Mozilla Bug 721136</a>
+<pre id="test">
+<script>
+[
+  [" mAtRiX(1, 2,3,4, 5,6 ) ", "matrix(1, 2, 3, 4, 5, 6)"],
+  [" mAtRiX3d( 1,2,3,0,4 ,5,6,0,7,8 , 9,0,10, 11,12,1 )  ",
+   "matrix3d(1, 2, 3, 0, 4, 5, 6, 0, 7, 8, 9, 0, 10, 11, 12, 1)"],
+  [" pErSpEcTiVe( 400Px ) ", "perspective(400px)"],
+  [" rOtAtE( 90dEg ) ", "rotate(90deg)"],
+  [" rOtAtE3d( 0,0 , 1 ,180DeG ) ", "rotate3d(0, 0, 1, 180deg)"],
+  [" rOtAtEx( 100GrAD ) ", "rotateX(100grad)"],
+  [" rOtAtEy( 1.57RaD ) ", "rotateY(1.57rad)"],
+  [" rOtAtEz( 0.25TuRn ) ", "rotateZ(0.25turn)"],
+  [" sCaLe( 2 ) ", "scale(2)"],
+  [" sCaLe( 2,3 ) ", "scale(2, 3)"],
+  [" sCaLe3D( 2,4 ,  -9 )  ", "scale3d(2, 4, -9)"],
+  [" sCaLeX( 2 ) ", "scaleX(2)"],
+  [" sCaLeY( 2 ) ", "scaleY(2)"],
+  [" sCaLeZ( 2 ) ", "scaleZ(2)"],
+  [" sKeW( 45dEg ) ", "skew(45deg)"],
+  [" sKeW( 45dEg,45DeG ) ", "skew(45deg, 45deg)"],
+  [" sKeWx( 45DeG ) ", "skewX(45deg)"],
+  [" sKeWy( 45DeG ) ", "skewY(45deg)"],
+  [" tRaNsLaTe( 1Px ) ", "translate(1px)"],
+  [" tRaNsLaTe( 1Px,3Pt ) ", "translate(1px, 3pt)"],
+  [" tRaNsLaTe3D( 21pX,-6pX , 4pX )  ", "translate3d(21px, -6px, 4px)"],
+  [" tRaNsLaTeX( 1pT ) ", "translateX(1pt)"],
+  [" tRaNsLaTeY( 1iN ) ", "translateY(1in)"],
+  [" tRaNsLaTeZ( 15.4pX ) ", "translateZ(15.4px)"],
+  ["tranSlatex( 16px )rotatez(-90deg)  rotate(100grad)\ttranslate3d(12pt, 0pc, 0.0em)",
+   "translateX(16px) rotateZ(-90deg) rotate(100grad) translate3d(12pt, 0pc, 0em)"],
+].forEach(function(arr) {
+  document.documentElement.style.MozTransform = arr[0];
+  is(document.documentElement.style.MozTransform, arr[1],
+    "incorrect serialization");
+});
+</script>
+</pre>
diff --git a/layout/style/test/test_keyframes_rules.html b/layout/style/test/test_keyframes_rules.html
index ca6a1f3c23bc..f31f057ef2b1 100644
--- a/layout/style/test/test_keyframes_rules.html
+++ b/layout/style/test/test_keyframes_rules.html
@@ -71,12 +71,12 @@ is(bounce.cssRules[0].keyText, "0%", "keyframe rule keyText");
 is(bounce.cssRules[1].keyText, "25%", "keyframe rule keyText");
 is(bounce.cssRules[2].keyText, "75%, 85%", "keyframe rule keyText");
 is(bounce.cssRules[3].keyText, "100%", "keyframe rule keyText");
-is(bounce.cssRules[0].style.marginLeft, "0pt", "keyframe rule style");
+is(bounce.cssRules[0].style.marginLeft, "0px", "keyframe rule style");
 is(bounce.cssRules[1].style.marginLeft, "25px", "keyframe rule style");
 
-is(bounce.cssRules[0].cssText, "0% { margin-left: 0pt; }");
+is(bounce.cssRules[0].cssText, "0% { margin-left: 0px; }");
 is(bounce.cssText, "@-moz-keyframes bouncier {\n" +
-                   "0% { margin-left: 0pt; }\n" +
+                   "0% { margin-left: 0px; }\n" +
                    "25% { margin-left: 25px; }\n" +
                    "75%, 85% { margin-left: 90px; }\n" +
                    "100% { margin-left: 100px; }\n" +
diff --git a/layout/style/test/test_units_length.html b/layout/style/test/test_units_length.html
index 378a98f35205..6db3f0c7dc6f 100644
--- a/layout/style/test/test_units_length.html
+++ b/layout/style/test/test_units_length.html
@@ -48,6 +48,11 @@ for (var test in tests) {
   }
 }
 
+// Bug 393910
+p.setAttribute("style", "margin-left: 0");
+is(p.style.getPropertyValue("margin-left"), "0px",
+  "0 serializes to 0px");
+
 </script>
 </pre>
 </body>
diff --git a/layout/svg/base/src/nsSVGForeignObjectFrame.cpp b/layout/svg/base/src/nsSVGForeignObjectFrame.cpp
index 67de74f79dba..35169c61fbcd 100644
--- a/layout/svg/base/src/nsSVGForeignObjectFrame.cpp
+++ b/layout/svg/base/src/nsSVGForeignObjectFrame.cpp
@@ -271,12 +271,6 @@ nsSVGForeignObjectFrame::PaintSVG(nsSVGRenderState *aContext,
 
   gfx->Multiply(matrixForChildren);
 
-  // Transform the dirty rect into the rectangle containing the
-  // transformed dirty rect.
-  gfxMatrix invmatrix = matrix.Invert();
-  NS_ASSERTION(!invmatrix.IsSingular(),
-               "inverse of non-singular matrix should be non-singular");
-
   PRUint32 flags = nsLayoutUtils::PAINT_IN_TRANSFORM;
   if (aContext->IsPaintingToWindow()) {
     flags |= nsLayoutUtils::PAINT_TO_WINDOW;
diff --git a/layout/svg/base/src/nsSVGImageFrame.cpp b/layout/svg/base/src/nsSVGImageFrame.cpp
index 9a2cd3205d6e..07e1eeb83dd2 100644
--- a/layout/svg/base/src/nsSVGImageFrame.cpp
+++ b/layout/svg/base/src/nsSVGImageFrame.cpp
@@ -52,7 +52,7 @@ using namespace mozilla;
 
 class nsSVGImageFrame;
 
-class nsSVGImageListener : public nsStubImageDecoderObserver
+class nsSVGImageListener MOZ_FINAL : public nsStubImageDecoderObserver
 {
 public:
   nsSVGImageListener(nsSVGImageFrame *aFrame);
diff --git a/media/libpng/pngrutil.c b/media/libpng/pngrutil.c
index 85386f2ad560..1f7af96df6dd 100644
--- a/media/libpng/pngrutil.c
+++ b/media/libpng/pngrutil.c
@@ -401,8 +401,15 @@ png_decompress_chunk(png_structp png_ptr, int comp_type,
       {
          /* Success (maybe) - really uncompress the chunk. */
          png_size_t new_size = 0;
-         png_charp text = png_malloc_warn(png_ptr,
-                        prefix_size + expanded_size + 1);
+         png_charp text = NULL;
+         /* Need to check for both truncation (64-bit platforms) and integer
+          * overflow.
+          */
+         if (prefix_size + expanded_size > prefix_size &&
+             prefix_size + expanded_size < 0xffffffffU)
+         {
+            text = png_malloc_warn(png_ptr, prefix_size + expanded_size + 1);
+         }
 
          if (text != NULL)
          {
diff --git a/mobile/android/base/AboutHomeContent.java b/mobile/android/base/AboutHomeContent.java
index bdf6e16c82f7..cf6a18ce335d 100644
--- a/mobile/android/base/AboutHomeContent.java
+++ b/mobile/android/base/AboutHomeContent.java
@@ -693,14 +693,14 @@ public class AboutHomeContent extends ScrollView {
             ImageView thumbnail = (ImageView) view;
 
             if (b == null) {
-                thumbnail.setImageResource(R.drawable.abouthome_topsite_placeholder);
+                thumbnail.setImageResource(R.drawable.tab_thumbnail_default);
             } else {
                 try {
                     Bitmap bitmap = BitmapFactory.decodeByteArray(b, 0, b.length);
                     thumbnail.setImageBitmap(bitmap);
                 } catch (OutOfMemoryError oom) {
                     Log.e(LOGTAG, "Unable to load thumbnail bitmap", oom);
-                    thumbnail.setImageResource(R.drawable.abouthome_topsite_placeholder);
+                    thumbnail.setImageResource(R.drawable.tab_thumbnail_default);
                 }
             }
 
diff --git a/mobile/android/base/AwesomeBarTabs.java b/mobile/android/base/AwesomeBarTabs.java
index d8d50d40a274..19524a3d1c35 100644
--- a/mobile/android/base/AwesomeBarTabs.java
+++ b/mobile/android/base/AwesomeBarTabs.java
@@ -284,7 +284,7 @@ public class AwesomeBarTabs extends TabHost {
 
             try {
                 // use reflection to disable auto-requery
-                Class cls = Class.forName("android.widget.CursorTreeAdapter"); 
+                Class<?> cls = Class.forName("android.widget.CursorTreeAdapter");
                 Field field = cls.getDeclaredField("mAutoRequery");
                 field.setAccessible(true);
                 field.set(mBookmarksAdapter, false);
diff --git a/mobile/android/base/BrowserToolbar.java b/mobile/android/base/BrowserToolbar.java
index ef373e60a440..8708e236de81 100644
--- a/mobile/android/base/BrowserToolbar.java
+++ b/mobile/android/base/BrowserToolbar.java
@@ -322,6 +322,20 @@ public class BrowserToolbar extends LinearLayout {
         }
     }
 
+    public void show() {
+        if (Build.VERSION.SDK_INT >= 11)
+            GeckoActionBar.show(GeckoApp.mAppContext);
+        else
+            setVisibility(View.VISIBLE);
+    }
+
+    public void hide() {
+        if (Build.VERSION.SDK_INT >= 11)
+            GeckoActionBar.hide(GeckoApp.mAppContext);
+        else
+            setVisibility(View.GONE);
+    }
+
     public void refresh() {
         Tab tab = Tabs.getInstance().getSelectedTab();
         if (tab != null) {
diff --git a/mobile/android/base/GeckoActionBar.java b/mobile/android/base/GeckoActionBar.java
index 9f1db9b7e021..4f707d8b6c65 100644
--- a/mobile/android/base/GeckoActionBar.java
+++ b/mobile/android/base/GeckoActionBar.java
@@ -66,4 +66,8 @@ public class GeckoActionBar {
     public static void setDisplayHomeAsUpEnabled(Activity activity, boolean enabled) {
          activity.getActionBar().setDisplayHomeAsUpEnabled(enabled);
     } 
+ 
+    public static void show(Activity activity) {
+        activity.getActionBar().show();
+    }
 }
diff --git a/mobile/android/base/GeckoApp.java b/mobile/android/base/GeckoApp.java
index a06f1cfead98..2698af98bde7 100644
--- a/mobile/android/base/GeckoApp.java
+++ b/mobile/android/base/GeckoApp.java
@@ -1024,13 +1024,13 @@ abstract public class GeckoApp
             } else if (event.equals("ToggleChrome:Hide")) {
                 mMainHandler.post(new Runnable() {
                     public void run() {
-                        mBrowserToolbar.setVisibility(View.GONE);
+                        mBrowserToolbar.hide();
                     }
                 });
             } else if (event.equals("ToggleChrome:Show")) {
                 mMainHandler.post(new Runnable() {
                     public void run() {
-                        mBrowserToolbar.setVisibility(View.VISIBLE);
+                        mBrowserToolbar.show();
                     }
                 });
             } else if (event.equals("DOMFullScreen:Start")) {
@@ -1507,7 +1507,8 @@ abstract public class GeckoApp
         if (tab == null)
             return;
 
-        tab.removePluginLayer(surface);
+        Layer layer = tab.removePluginLayer(surface);
+        hidePluginLayer(layer);
     }
 
     public void showSurface(Surface surface, int x, int y,
@@ -1642,9 +1643,12 @@ abstract public class GeckoApp
         mMainHandler.post(new Runnable() { 
             public void run() {
                 // Hide/show the system notification bar
-                getWindow().setFlags(fullscreen ?
-                                     WindowManager.LayoutParams.FLAG_FULLSCREEN : 0,
-                                     WindowManager.LayoutParams.FLAG_FULLSCREEN);
+                Window window = getWindow();
+                window.setFlags(fullscreen ?
+                                WindowManager.LayoutParams.FLAG_FULLSCREEN : 0,
+                                WindowManager.LayoutParams.FLAG_FULLSCREEN);
+
+                window.getDecorView().setSystemUiVisibility(fullscreen ? 1 : 0);
             }
         });
     }
@@ -2087,7 +2091,7 @@ abstract public class GeckoApp
         Runnable r = new SessionSnapshotRunnable(null);
         GeckoAppShell.getHandler().post(r);
 
-        GeckoAppShell.sendEventToGecko(GeckoEvent.createPauseEvent());
+        GeckoAppShell.sendEventToGecko(GeckoEvent.createPauseEvent(mOwnActivityDepth));
         // The user is navigating away from this activity, but nothing
         // has come to the foreground yet; for Gecko, we may want to
         // stop repainting, for example.
@@ -2107,7 +2111,8 @@ abstract public class GeckoApp
     {
         Log.i(LOGTAG, "resume");
         if (checkLaunchState(LaunchState.GeckoRunning))
-            GeckoAppShell.onResume();
+            GeckoAppShell.sendEventToGecko(GeckoEvent.createResumeEvent(mOwnActivityDepth));
+
         // After an onPause, the activity is back in the foreground.
         // Undo whatever we did in onPause.
         super.onResume();
@@ -2156,7 +2161,7 @@ abstract public class GeckoApp
         // etc., and generally mark the profile as 'clean', and then
         // dirty it again if we get an onResume.
 
-        GeckoAppShell.sendEventToGecko(GeckoEvent.createStoppingEvent());
+        GeckoAppShell.sendEventToGecko(GeckoEvent.createStoppingEvent(mOwnActivityDepth));
         super.onStop();
     }
 
@@ -2173,7 +2178,7 @@ abstract public class GeckoApp
         Log.w(LOGTAG, "zerdatime " + SystemClock.uptimeMillis() + " - onStart");
 
         Log.i(LOGTAG, "start");
-        GeckoAppShell.sendEventToGecko(GeckoEvent.createStartEvent());
+        GeckoAppShell.sendEventToGecko(GeckoEvent.createStartEvent(mOwnActivityDepth));
         super.onStart();
     }
 
diff --git a/mobile/android/base/GeckoEvent.java b/mobile/android/base/GeckoEvent.java
index 30701e3b3784..eb622c7770d1 100644
--- a/mobile/android/base/GeckoEvent.java
+++ b/mobile/android/base/GeckoEvent.java
@@ -87,6 +87,7 @@ public class GeckoEvent {
     private static final int VISITED = 21;
     private static final int NETWORK_CHANGED = 22;
     private static final int PROXIMITY_EVENT = 23;
+    private static final int ACTIVITY_RESUMING = 24;
 
     public static final int IME_COMPOSITION_END = 0;
     public static final int IME_COMPOSITION_BEGIN = 1;
@@ -139,16 +140,28 @@ public class GeckoEvent {
         mType = evType;
     }
 
-    public static GeckoEvent createPauseEvent() {
-        return new GeckoEvent(ACTIVITY_PAUSING);
+    public static GeckoEvent createPauseEvent(int activityDepth) {
+        GeckoEvent event = new GeckoEvent(ACTIVITY_PAUSING);
+        event.mFlags = activityDepth > 0 ? 1 : 0;
+        return event;
     }
 
-    public static GeckoEvent createStoppingEvent() {
-        return new GeckoEvent(ACTIVITY_STOPPING);
+    public static GeckoEvent createResumeEvent(int activityDepth) {
+        GeckoEvent event = new GeckoEvent(ACTIVITY_RESUMING);
+        event.mFlags = activityDepth > 0 ? 1 : 0;
+        return event;
     }
 
-    public static GeckoEvent createStartEvent() {
-        return new GeckoEvent(ACTIVITY_START);
+    public static GeckoEvent createStoppingEvent(int activityDepth) {
+        GeckoEvent event = new GeckoEvent(ACTIVITY_STOPPING);
+        event.mFlags = activityDepth > 0 ? 1 : 0;
+        return event;
+    }
+
+    public static GeckoEvent createStartEvent(int activityDepth) {
+        GeckoEvent event = new GeckoEvent(ACTIVITY_START);
+        event.mFlags = activityDepth > 0 ? 1 : 0;
+        return event;
     }
 
     public static GeckoEvent createShutdownEvent() {
diff --git a/mobile/android/base/Makefile.in b/mobile/android/base/Makefile.in
index 08bcb5d9140a..70d0d8ab0cb6 100644
--- a/mobile/android/base/Makefile.in
+++ b/mobile/android/base/Makefile.in
@@ -295,8 +295,6 @@ RES_DRAWABLE_BASE = \
   res/drawable/abouthome_sync_logo.png \
   res/drawable/abouthome_sync_bg.9.png \
   res/drawable/abouthome_sync_pressed_bg.9.png \
-  res/drawable/abouthome_topsite_placeholder.png \
-  res/drawable/abouthome_topsite_shadow.9.png \
   res/drawable/awesomebar_tab.9.png \
   res/drawable/awesomebar_tab_pressed.9.png \
   res/drawable/ic_addons_empty.png \
@@ -345,8 +343,6 @@ RES_DRAWABLE_HDPI = \
   res/drawable-hdpi/abouthome_sync_logo.png \
   res/drawable-hdpi/abouthome_sync_bg.9.png \
   res/drawable-hdpi/abouthome_sync_pressed_bg.9.png \
-  res/drawable-hdpi/abouthome_topsite_placeholder.png \
-  res/drawable-hdpi/abouthome_topsite_shadow.9.png \
   res/drawable-hdpi/awesomebar_tab.9.png \
   res/drawable-hdpi/awesomebar_tab_pressed.9.png \
   res/drawable-hdpi/ic_addons_empty.png \
@@ -409,8 +405,6 @@ RES_DRAWABLE_XHDPI_V11 = \
   res/drawable-xhdpi-v11/abouthome_sync_logo.png \
   res/drawable-xhdpi-v11/abouthome_sync_bg.9.png \
   res/drawable-xhdpi-v11/abouthome_sync_pressed_bg.9.png \
-  res/drawable-xhdpi-v11/abouthome_topsite_placeholder.png \
-  res/drawable-xhdpi-v11/abouthome_topsite_shadow.9.png \
   res/drawable-xhdpi-v11/awesomebar_tab.9.png \
   res/drawable-xhdpi-v11/awesomebar_tab_pressed.9.png \
   res/drawable-xhdpi-v11/ic_addons_empty.png \
diff --git a/mobile/android/base/db/AndroidBrowserDB.java b/mobile/android/base/db/AndroidBrowserDB.java
index b2edaa9403a2..ca52ca37e007 100644
--- a/mobile/android/base/db/AndroidBrowserDB.java
+++ b/mobile/android/base/db/AndroidBrowserDB.java
@@ -188,23 +188,6 @@ public class AndroidBrowserDB implements BrowserDB.BrowserDBIface {
         Browser.clearHistory(cr);
     }
 
-    public Cursor getAllBookmarks(ContentResolver cr) {
-        Cursor c = cr.query(Browser.BOOKMARKS_URI,
-                            new String[] { URL_COLUMN_ID,
-                                           BookmarkColumns.URL,
-                                           BookmarkColumns.TITLE,
-                                           BookmarkColumns.FAVICON },
-                            // Select all bookmarks with a non-empty URL. When the history
-                            // is empty there appears to be a dummy entry in the database
-                            // which has a title of "Bookmarks" and no URL; the length restriction
-                            // is to avoid picking that up specifically.
-                            Browser.BookmarkColumns.BOOKMARK + " = 1 AND LENGTH(" + Browser.BookmarkColumns.URL + ") > 0",
-                            null,
-                            Browser.BookmarkColumns.TITLE);
-
-        return new AndroidDBCursor(c);
-    }
-
     public Cursor getMobileBookmarks(ContentResolver cr) {
         Cursor c = cr.query(null, null, null, null, null);
         return new AndroidDBCursor(c);
diff --git a/mobile/android/base/db/BrowserDB.java b/mobile/android/base/db/BrowserDB.java
index 80a80ba015d0..922e6e8d7f30 100644
--- a/mobile/android/base/db/BrowserDB.java
+++ b/mobile/android/base/db/BrowserDB.java
@@ -77,8 +77,6 @@ public class BrowserDB {
 
         public void clearHistory(ContentResolver cr);
 
-        public Cursor getAllBookmarks(ContentResolver cr);
-
         public Cursor getMobileBookmarks(ContentResolver cr);
 
         public Cursor getDesktopBookmarks(ContentResolver cr);
@@ -148,10 +146,6 @@ public class BrowserDB {
         sDb.clearHistory(cr);
     }
 
-    public static Cursor getAllBookmarks(ContentResolver cr) {
-        return sDb.getAllBookmarks(cr);
-    }
-
     public static Cursor getMobileBookmarks(ContentResolver cr) {
         return sDb.getMobileBookmarks(cr);
     }
diff --git a/mobile/android/base/db/LocalBrowserDB.java b/mobile/android/base/db/LocalBrowserDB.java
index 60906a18d71e..231920710213 100644
--- a/mobile/android/base/db/LocalBrowserDB.java
+++ b/mobile/android/base/db/LocalBrowserDB.java
@@ -80,14 +80,28 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
     private final String mProfile;
     private long mMobileFolderId;
 
+    private final Uri mBookmarksUriWithProfile;
+    private final Uri mParentsUriWithProfile;
+    private final Uri mHistoryUriWithProfile;
+    private final Uri mImagesUriWithProfile;
+    private final Uri mDeletedHistoryUriWithProfile;
+
     public LocalBrowserDB(String profile) {
         mProfile = profile;
         mMobileFolderId = -1;
+
+        mBookmarksUriWithProfile = appendProfile(Bookmarks.CONTENT_URI);
+        mParentsUriWithProfile = appendProfile(Bookmarks.PARENTS_CONTENT_URI);
+        mHistoryUriWithProfile = appendProfile(History.CONTENT_URI);
+        mImagesUriWithProfile = appendProfile(Images.CONTENT_URI);
+
+        mDeletedHistoryUriWithProfile = mHistoryUriWithProfile.buildUpon().
+            appendQueryParameter(BrowserContract.PARAM_SHOW_DELETED, "1").build();
     }
 
-    private Uri appendProfileAndLimit(Uri uri, int limit) {
-        return uri.buildUpon().appendQueryParameter(BrowserContract.PARAM_PROFILE, mProfile).
-                appendQueryParameter(BrowserContract.PARAM_LIMIT, String.valueOf(limit)).build();
+    private Uri historyUriWithLimit(int limit) {
+        return mHistoryUriWithProfile.buildUpon().appendQueryParameter(BrowserContract.PARAM_LIMIT,
+                                                                       String.valueOf(limit)).build();
     }
 
     private Uri appendProfile(Uri uri) {
@@ -95,7 +109,7 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
     }
 
     private Cursor filterAllSites(ContentResolver cr, String[] projection, CharSequence constraint, int limit, CharSequence urlFilter) {
-        Cursor c = cr.query(appendProfileAndLimit(History.CONTENT_URI, limit),
+        Cursor c = cr.query(historyUriWithLimit(limit),
                             projection,
                             (urlFilter != null ? "(" + History.URL + " NOT LIKE ? ) AND " : "" ) + 
                             "(" + History.URL + " LIKE ? OR " + History.TITLE + " LIKE ?)",
@@ -136,7 +150,7 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
         Cursor cursor = null;
 
         try {
-            cursor = cr.query(appendProfile(History.CONTENT_URI),
+            cursor = cr.query(mHistoryUriWithProfile,
                               new String[] { History._ID },
                               null,
                               null,
@@ -170,7 +184,7 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
                     History.VISITS, // 1
             };
 
-            cursor = cr.query(appendProfile(History.CONTENT_URI),
+            cursor = cr.query(mDeletedHistoryUriWithProfile,
                               projection,
                               History.URL + " = ?",
                               new String[] { uri },
@@ -182,6 +196,9 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
                 values.put(History.VISITS, cursor.getInt(1) + 1);
                 values.put(History.DATE_LAST_VISITED, now);
 
+                // Restore deleted record if possible
+                values.put(History.IS_DELETED, 0);
+
                 Uri historyUri = ContentUris.withAppendedId(History.CONTENT_URI, cursor.getLong(0));
                 cr.update(appendProfile(historyUri), values, null, null);
             } else {
@@ -196,7 +213,7 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
                 values.put(History.DATE_LAST_VISITED, now);
                 values.put(History.TITLE, uri);
 
-                cr.insert(appendProfile(History.CONTENT_URI), values);
+                cr.insert(mHistoryUriWithProfile, values);
             }
         } finally {
             if (cursor != null)
@@ -208,7 +225,7 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
         ContentValues values = new ContentValues();
         values.put(History.TITLE, title);
 
-        cr.update(appendProfile(History.CONTENT_URI),
+        cr.update(mHistoryUriWithProfile,
                   values,
                   History.URL + " = ?",
                   new String[] { uri });
@@ -219,7 +236,7 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
         int oldVisits = 0;
         Cursor cursor = null;
         try {
-            cursor = cr.query(appendProfile(History.CONTENT_URI),
+            cursor = cr.query(mHistoryUriWithProfile,
                               new String[] { History.VISITS },
                               History.URL + " = ?",
                               new String[] { uri },
@@ -240,14 +257,14 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
             values.put(History.TITLE, title);
         }
 
-        cr.update(appendProfile(History.CONTENT_URI),
+        cr.update(mHistoryUriWithProfile,
                   values,
                   History.URL + " = ?",
                   new String[] { uri });
     }
 
     public Cursor getAllVisitedHistory(ContentResolver cr) {
-        Cursor c = cr.query(appendProfile(History.CONTENT_URI),
+        Cursor c = cr.query(mHistoryUriWithProfile,
                             new String[] { History.URL },
                             History.VISITS + " > 0",
                             null,
@@ -257,7 +274,7 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
     }
 
     public Cursor getRecentHistory(ContentResolver cr, int limit) {
-        Cursor c = cr.query(appendProfileAndLimit(History.CONTENT_URI, limit),
+        Cursor c = cr.query(historyUriWithLimit(limit),
                             new String[] { History._ID,
                                            History.URL,
                                            History.TITLE,
@@ -276,21 +293,7 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
     }
 
     public void clearHistory(ContentResolver cr) {
-        cr.delete(appendProfile(History.CONTENT_URI), null, null);
-    }
-
-    public Cursor getAllBookmarks(ContentResolver cr) {
-        Cursor c = cr.query(appendProfile(Bookmarks.CONTENT_URI),
-                            new String[] { Bookmarks._ID,
-                                           Bookmarks.URL,
-                                           Bookmarks.TITLE,
-                                           Bookmarks.FAVICON,
-                                           Bookmarks.KEYWORD },
-                            Bookmarks.IS_FOLDER + " = 0",
-                            null,
-                            Bookmarks.TITLE + " ASC");
-
-        return new LocalDBCursor(c);
+        cr.delete(mHistoryUriWithProfile, null, null);
     }
 
     public Cursor getMobileBookmarks(ContentResolver cr) {
@@ -304,7 +307,7 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
     private Cursor getBookmarks(ContentResolver cr, boolean mobileBookmarks) {
         String parentSelection = mobileBookmarks ? " = ?" : " != ?";
         long mobileFolderId = getMobileBookmarksFolderId(cr);
-        Cursor c = cr.query(appendProfile(Bookmarks.CONTENT_URI),
+        Cursor c = cr.query(mBookmarksUriWithProfile,
                             new String[] { Bookmarks._ID,
                                            Bookmarks.URL,
                                            Bookmarks.TITLE,
@@ -318,7 +321,7 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
     }
 
     public boolean isBookmark(ContentResolver cr, String uri) {
-        Cursor cursor = cr.query(appendProfile(Bookmarks.CONTENT_URI),
+        Cursor cursor = cr.query(mBookmarksUriWithProfile,
                                  new String[] { Bookmarks._ID },
                                  Bookmarks.URL + " = ?",
                                  new String[] { uri },
@@ -331,7 +334,7 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
     }
 
     public String getUrlForKeyword(ContentResolver cr, String keyword) {
-        Cursor cursor = cr.query(appendProfile(Bookmarks.CONTENT_URI),
+        Cursor cursor = cr.query(mBookmarksUriWithProfile,
                                  new String[] { Bookmarks.URL },
                                  Bookmarks.KEYWORD + " = ?",
                                  new String[] { keyword },
@@ -355,7 +358,7 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
         Cursor c = null;
 
         try {
-            c = cr.query(appendProfile(Bookmarks.CONTENT_URI),
+            c = cr.query(mBookmarksUriWithProfile,
                          new String[] { Bookmarks._ID },
                          Bookmarks.GUID + " = ?",
                          new String[] { Bookmarks.MOBILE_FOLDER_GUID },
@@ -379,15 +382,9 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
         ContentValues values = new ContentValues();
         values.put(Bookmarks.DATE_MODIFIED, System.currentTimeMillis());
 
-        // Inline appendProfile here to avoid building multiple Uri objects.
-        final Uri updateURI =
-            Bookmarks.CONTENT_URI.buildUpon()
-                                 .appendPath("parents")
-                                 .appendQueryParameter(BrowserContract.PARAM_PROFILE, mProfile)
-                                 .build();
         String where  = param + " = ?";
         String[] args = new String[] { value };
-        int updated  = cr.update(updateURI, values, where, args);
+        int updated  = cr.update(mParentsUriWithProfile, values, where, args);
         debug("Updated " + updated + " rows to new modified time.");
     }
 
@@ -406,7 +403,7 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
         // Restore deleted record if possible
         values.put(Bookmarks.IS_DELETED, 0);
 
-        Uri contentUri = appendProfile(Bookmarks.CONTENT_URI);
+        Uri contentUri = mBookmarksUriWithProfile;
         int updated = cr.update(contentUri,
                                 values,
                                 Bookmarks.URL + " = ?",
@@ -430,7 +427,7 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
     }
 
     public void removeBookmark(ContentResolver cr, int id) {
-        Uri contentUri = appendProfile(Bookmarks.CONTENT_URI);
+        Uri contentUri = mBookmarksUriWithProfile;
 
         // Do this now so that the item still exists!
         final String idString = String.valueOf(id);
@@ -444,7 +441,7 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
     }
 
     public void removeBookmarksWithURL(ContentResolver cr, String uri) {
-        Uri contentUri = appendProfile(Bookmarks.CONTENT_URI);
+        Uri contentUri = mBookmarksUriWithProfile;
 
         // Do this now so that the items still exist!
         bumpParents(cr, Bookmarks.URL, uri);
@@ -457,7 +454,7 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
     }
 
     public void registerBookmarkObserver(ContentResolver cr, ContentObserver observer) {
-        Uri uri = appendProfile(Bookmarks.CONTENT_URI);
+        Uri uri = mBookmarksUriWithProfile;
         cr.registerContentObserver(uri, false, observer);
     }
 
@@ -467,14 +464,14 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
         values.put(Bookmarks.URL, uri);
         values.put(Bookmarks.KEYWORD, keyword);
 
-        cr.update(appendProfile(Bookmarks.CONTENT_URI),
+        cr.update(mBookmarksUriWithProfile,
                   values,
                   Bookmarks.URL + " = ?",
                   new String[] { oldUri });
     }
 
     public BitmapDrawable getFaviconForUrl(ContentResolver cr, String uri) {
-        Cursor c = cr.query(appendProfile(Images.CONTENT_URI),
+        Cursor c = cr.query(mImagesUriWithProfile,
                             new String[] { Images.FAVICON },
                             Images.URL + " = ?",
                             new String[] { uri },
@@ -511,13 +508,13 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
         // Restore deleted record if possible
         values.put(Images.IS_DELETED, 0);
 
-        int updated = cr.update(appendProfile(Images.CONTENT_URI),
+        int updated = cr.update(mImagesUriWithProfile,
                                 values,
                                 Images.URL + " = ?",
                                 new String[] { uri });
 
         if (updated == 0)
-            cr.insert(appendProfile(Images.CONTENT_URI), values);
+            cr.insert(mImagesUriWithProfile, values);
     }
 
     public void updateThumbnailForUrl(ContentResolver cr, String uri,
@@ -534,17 +531,17 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
         // Restore deleted record if possible
         values.put(Images.IS_DELETED, 0);
 
-        int updated = cr.update(appendProfile(Images.CONTENT_URI),
+        int updated = cr.update(mImagesUriWithProfile,
                                 values,
                                 Images.URL + " = ?",
                                 new String[] { uri });
 
         if (updated == 0)
-            cr.insert(appendProfile(Images.CONTENT_URI), values);
+            cr.insert(mImagesUriWithProfile, values);
     }
 
     public byte[] getThumbnailForUrl(ContentResolver cr, String uri) {
-        Cursor c = cr.query(appendProfile(Images.CONTENT_URI),
+        Cursor c = cr.query(mImagesUriWithProfile,
                             new String[] { Images.THUMBNAIL },
                             Images.URL + " = ?",
                             new String[] { uri },
diff --git a/mobile/android/base/resources/drawable-hdpi/abouthome_topsite_placeholder.png b/mobile/android/base/resources/drawable-hdpi/abouthome_topsite_placeholder.png
deleted file mode 100644
index 82452a943b7d..000000000000
Binary files a/mobile/android/base/resources/drawable-hdpi/abouthome_topsite_placeholder.png and /dev/null differ
diff --git a/mobile/android/base/resources/drawable-hdpi/abouthome_topsite_shadow.9.png b/mobile/android/base/resources/drawable-hdpi/abouthome_topsite_shadow.9.png
deleted file mode 100644
index c3afbe3115cc..000000000000
Binary files a/mobile/android/base/resources/drawable-hdpi/abouthome_topsite_shadow.9.png and /dev/null differ
diff --git a/mobile/android/base/resources/drawable-xhdpi-v11/abouthome_topsite_placeholder.png b/mobile/android/base/resources/drawable-xhdpi-v11/abouthome_topsite_placeholder.png
deleted file mode 100644
index 6413a45633ee..000000000000
Binary files a/mobile/android/base/resources/drawable-xhdpi-v11/abouthome_topsite_placeholder.png and /dev/null differ
diff --git a/mobile/android/base/resources/drawable-xhdpi-v11/abouthome_topsite_shadow.9.png b/mobile/android/base/resources/drawable-xhdpi-v11/abouthome_topsite_shadow.9.png
deleted file mode 100644
index bad57a716659..000000000000
Binary files a/mobile/android/base/resources/drawable-xhdpi-v11/abouthome_topsite_shadow.9.png and /dev/null differ
diff --git a/mobile/android/base/resources/drawable/abouthome_topsite_placeholder.png b/mobile/android/base/resources/drawable/abouthome_topsite_placeholder.png
deleted file mode 100644
index 7ae2b283350a..000000000000
Binary files a/mobile/android/base/resources/drawable/abouthome_topsite_placeholder.png and /dev/null differ
diff --git a/mobile/android/base/resources/drawable/abouthome_topsite_shadow.9.png b/mobile/android/base/resources/drawable/abouthome_topsite_shadow.9.png
deleted file mode 100644
index 478fb92498e7..000000000000
Binary files a/mobile/android/base/resources/drawable/abouthome_topsite_shadow.9.png and /dev/null differ
diff --git a/mobile/android/base/resources/layout/abouthome_topsite_item.xml b/mobile/android/base/resources/layout/abouthome_topsite_item.xml
index 19e3afcc7caa..c4860aa6a61a 100644
--- a/mobile/android/base/resources/layout/abouthome_topsite_item.xml
+++ b/mobile/android/base/resources/layout/abouthome_topsite_item.xml
@@ -10,7 +10,7 @@
         <LinearLayout android:orientation="vertical"
                       android:layout_width="138dip"
                       android:layout_height="80dip"
-                      android:background="@drawable/abouthome_topsite_shadow"
+                      android:background="@drawable/tab_thumbnail_shadow"
                       android:padding="1dip"
                       android:paddingBottom="2dip">
 
diff --git a/mobile/android/base/ui/PanZoomController.java b/mobile/android/base/ui/PanZoomController.java
index bfd0b43e7d05..04a182f0d157 100644
--- a/mobile/android/base/ui/PanZoomController.java
+++ b/mobile/android/base/ui/PanZoomController.java
@@ -297,7 +297,7 @@ public class PanZoomController
             cancelTouch();
             startPanning(event.getX(0), event.getY(0), event.getEventTime());
             GeckoApp.mAppContext.hidePlugins(false /* don't hide layers */);
-            GeckoApp.mAppContext.mAutoCompletePopup.hide();
+            GeckoApp.mAutoCompletePopup.hide();
             track(event);
             return true;
 
diff --git a/mobile/xul/installer/package-manifest.in b/mobile/xul/installer/package-manifest.in
index 32a7f989175f..e03157b5edd8 100644
--- a/mobile/xul/installer/package-manifest.in
+++ b/mobile/xul/installer/package-manifest.in
@@ -157,6 +157,9 @@
 @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
diff --git a/netwerk/protocol/http/SpdySession.cpp b/netwerk/protocol/http/SpdySession.cpp
index d5ae8031b093..b592a6ef4236 100644
--- a/netwerk/protocol/http/SpdySession.cpp
+++ b/netwerk/protocol/http/SpdySession.cpp
@@ -1949,6 +1949,40 @@ SpdySession::Http1xTransactionCount()
   return 0;
 }
 
+// used as an enumerator by TakeSubTransactions()
+static PLDHashOperator
+TakeStream(nsAHttpTransaction *key,
+           nsAutoPtr<SpdyStream> &stream,
+           void *closure)
+{
+  nsTArray<nsRefPtr<nsAHttpTransaction> > *list =
+    static_cast<nsTArray<nsRefPtr<nsAHttpTransaction> > *>(closure);
+
+  list->AppendElement(key);
+
+  // removing the stream from the hash will delete the stream
+  // and drop the transaction reference the hash held
+  return PL_DHASH_REMOVE;
+}
+
+nsresult
+SpdySession::TakeSubTransactions(
+    nsTArray<nsRefPtr<nsAHttpTransaction> > &outTransactions)
+{
+  // Generally this cannot be done with spdy as transactions are
+  // started right away.
+
+  LOG3(("SpdySession::TakeSubTransactions %p\n", this));
+
+  if (mConcurrentHighWater > 0)
+    return NS_ERROR_ALREADY_OPENED;
+
+  LOG3(("   taking %d\n", mStreamTransactionHash.Count()));
+
+  mStreamTransactionHash.Enumerate(TakeStream, &outTransactions);
+  return NS_OK;
+}
+
 //-----------------------------------------------------------------------------
 // Pass through methods of nsAHttpConnection
 //-----------------------------------------------------------------------------
diff --git a/netwerk/protocol/http/nsAHttpTransaction.h b/netwerk/protocol/http/nsAHttpTransaction.h
index d859cfcf87a5..4a7980d2ad48 100644
--- a/netwerk/protocol/http/nsAHttpTransaction.h
+++ b/netwerk/protocol/http/nsAHttpTransaction.h
@@ -39,6 +39,7 @@
 #define nsAHttpTransaction_h__
 
 #include "nsISupports.h"
+#include "nsTArray.h"
 
 class nsAHttpConnection;
 class nsAHttpSegmentReader;
@@ -101,6 +102,18 @@ public:
     // abstract object. Pipelines may have multiple, SPDY has 0,
     // normal http transactions have 1.
     virtual PRUint32 Http1xTransactionCount() = 0;
+
+    // called to remove the unused sub transactions from an object that can
+    // handle multiple transactions simultaneously (i.e. pipelines or spdy).
+    //
+    // Returns NS_ERROR_NOT_IMPLEMENTED if the object does not implement
+    // sub-transactions.
+    //
+    // Returns NS_ERROR_ALREADY_OPENED if the subtransactions have been
+    // at least partially written and cannot be moved.
+    //
+    virtual nsresult TakeSubTransactions(
+        nsTArray<nsRefPtr<nsAHttpTransaction> > &outTransactions) = 0;
 };
 
 #define NS_DECL_NSAHTTPTRANSACTION \
@@ -118,7 +131,8 @@ public:
     void     Close(nsresult reason);                                    \
     void     SetSSLConnectFailed();                                     \
     nsHttpRequestHead *RequestHead();                                   \
-    PRUint32 Http1xTransactionCount();
+    PRUint32 Http1xTransactionCount();                                  \
+    nsresult TakeSubTransactions(nsTArray<nsRefPtr<nsAHttpTransaction> > &outTransactions);
 
 //-----------------------------------------------------------------------------
 // nsAHttpSegmentReader
diff --git a/netwerk/protocol/http/nsHttpChannelAuthProvider.cpp b/netwerk/protocol/http/nsHttpChannelAuthProvider.cpp
index 3cdece64df88..6acce884ff58 100644
--- a/netwerk/protocol/http/nsHttpChannelAuthProvider.cpp
+++ b/netwerk/protocol/http/nsHttpChannelAuthProvider.cpp
@@ -128,13 +128,16 @@ nsHttpChannelAuthProvider::ProcessAuthentication(PRUint32 httpStatus,
     rv = mAuthChannel->GetLoadFlags(&loadFlags);
     if (NS_FAILED(rv)) return rv;
 
-    if (loadFlags & nsIRequest::LOAD_ANONYMOUS) {
-        return NS_ERROR_NOT_AVAILABLE;
-    }
-
     nsCAutoString challenges;
     mProxyAuth = (httpStatus == 407);
 
+    // Do proxy auth even if we're LOAD_ANONYMOUS
+    if ((loadFlags & nsIRequest::LOAD_ANONYMOUS) &&
+        (!mProxyAuth || !UsingHttpProxy())) {
+        LOG(("Skipping authentication for anonymous non-proxy request\n"));
+        return NS_ERROR_NOT_AVAILABLE;
+    }
+
     rv = PrepareForAuthentication(mProxyAuth);
     if (NS_FAILED(rv))
         return rv;
@@ -199,10 +202,6 @@ nsHttpChannelAuthProvider::AddAuthorizationHeaders()
     rv = mAuthChannel->GetLoadFlags(&loadFlags);
     if (NS_FAILED(rv)) return rv;
 
-    if (loadFlags & nsIRequest::LOAD_ANONYMOUS) {
-        return NS_OK;
-    }
-
     // this getter never fails
     nsHttpAuthCache *authCache = gHttpHandler->AuthCache();
 
@@ -214,6 +213,11 @@ nsHttpChannelAuthProvider::AddAuthorizationHeaders()
                                nsnull, // proxy has no path
                                mProxyIdent);
 
+    if (loadFlags & nsIRequest::LOAD_ANONYMOUS) {
+        LOG(("Skipping Authorization header for anonymous load\n"));
+        return NS_OK;
+    }
+
     // check if server credentials should be sent
     nsCAutoString path, scheme;
     if (NS_SUCCEEDED(GetCurrentPath(path)) &&
diff --git a/netwerk/protocol/http/nsHttpConnection.cpp b/netwerk/protocol/http/nsHttpConnection.cpp
index 51d34110ecd9..d47f0401a678 100644
--- a/netwerk/protocol/http/nsHttpConnection.cpp
+++ b/netwerk/protocol/http/nsHttpConnection.cpp
@@ -170,6 +170,93 @@ nsHttpConnection::Init(nsHttpConnectionInfo *info,
     return NS_OK;
 }
 
+void
+nsHttpConnection::StartSpdy()
+{
+    LOG(("nsHttpConnection::StartSpdy [this=%p]\n", this));
+
+    NS_ABORT_IF_FALSE(!mSpdySession, "mSpdySession should be null");
+
+    mUsingSpdy = true;
+    mEverUsedSpdy = true;
+
+    // Setting the connection as reused allows some transactions that fail
+    // with NS_ERROR_NET_RESET to be restarted and SPDY uses that code
+    // to handle clean rejections (such as those that arrived after
+    // a server goaway was generated).
+    mIsReused = true;
+
+    // If mTransaction is a pipeline object it might represent
+    // several requests. If so, we need to unpack that and
+    // pack them all into a new spdy session.
+
+    nsTArray<nsRefPtr<nsAHttpTransaction> > list;
+    nsresult rv = mTransaction->TakeSubTransactions(list);
+
+    if (rv == NS_ERROR_ALREADY_OPENED) {
+        // Has the interface for TakeSubTransactions() changed?
+        LOG(("TakeSubTranscations somehow called after "
+             "nsAHttpTransaction began processing\n"));
+        NS_ABORT_IF_FALSE(false,
+                          "TakeSubTranscations somehow called after "
+                          "nsAHttpTransaction began processing");
+        mTransaction->Close(NS_ERROR_ABORT);
+        return;
+    }
+
+    if (NS_FAILED(rv) && rv != NS_ERROR_NOT_IMPLEMENTED) {
+        // Has the interface for TakeSubTransactions() changed?
+        LOG(("unexpected rv from nnsAHttpTransaction::TakeSubTransactions()"));
+        NS_ABORT_IF_FALSE(false,
+                          "unexpected result from "
+                          "nsAHttpTransaction::TakeSubTransactions()");
+        mTransaction->Close(NS_ERROR_ABORT);
+        return;
+    }
+
+    if (NS_FAILED(rv)) { // includes NS_ERROR_NOT_IMPLEMENTED
+        NS_ABORT_IF_FALSE(list.IsEmpty(), "sub transaction list not empty");
+
+        // This is ok - treat mTransaction as a single real request.
+        // Wrap the old http transaction into the new spdy session
+        // as the first stream.
+        mSpdySession = new SpdySession(mTransaction,
+                                       mSocketTransport,
+                                       mPriority);
+        LOG(("nsHttpConnection::StartSpdy moves single transaction %p "
+             "into SpdySession %p\n", mTransaction.get(), mSpdySession.get()));
+    }
+    else {
+        NS_ABORT_IF_FALSE(!list.IsEmpty(), "sub transaction list empty");
+        
+        PRInt32 count = list.Length();
+
+        LOG(("nsHttpConnection::StartSpdy moving transaction list len=%d "
+             "into SpdySession %p\n", count, mSpdySession.get()));
+
+        for (PRInt32 index = 0; index < count; ++index) {
+            if (!mSpdySession) {
+                mSpdySession = new SpdySession(list[index],
+                                               mSocketTransport,
+                                               mPriority);
+            }
+            else {
+                // AddStream() cannot fail
+                if (!mSpdySession->AddStream(list[index], mPriority)) {
+                    NS_ABORT_IF_FALSE(false, "SpdySession::AddStream failed");
+                    LOG(("SpdySession::AddStream failed\n"));
+                    mTransaction->Close(NS_ERROR_ABORT);
+                    return;
+                }
+            }
+        }
+    }
+
+    mSupportsPipelining = false; // dont use http/1 pipelines with spdy
+    mTransaction = mSpdySession;
+    mIdleTimeout = gHttpHandler->SpdyTimeout();
+}
+
 bool
 nsHttpConnection::EnsureNPNComplete()
 {
@@ -215,31 +302,15 @@ nsHttpConnection::EnsureNPNComplete()
             goto npnComplete;
         return false;
     }
-    
+
     if (NS_FAILED(rv))
         goto npnComplete;
 
     LOG(("nsHttpConnection::EnsureNPNComplete %p negotiated to '%s'",
          this, negotiatedNPN.get()));
-    
-    if (negotiatedNPN.Equals(NS_LITERAL_CSTRING("spdy/2"))) {
-        mUsingSpdy = true;
-        mEverUsedSpdy = true;
 
-        // Setting the connection as reused allows some transactions that fail
-        // with NS_ERROR_NET_RESET to be restarted and SPDY uses that code
-        // to handle clean rejections (such as those that arrived after
-        // a server goaway was generated).
-        mIsReused = true;
-
-        // Wrap the old http transaction into the new spdy session
-        // as the first stream
-        mSpdySession = new SpdySession(mTransaction,
-                                       mSocketTransport,
-                                       mPriority);
-        mTransaction = mSpdySession;
-        mIdleTimeout = gHttpHandler->SpdyTimeout();
-    }
+    if (negotiatedNPN.Equals(NS_LITERAL_CSTRING("spdy/2")))
+        StartSpdy();
 
     mozilla::Telemetry::Accumulate(mozilla::Telemetry::SPDY_NPN_CONNECT,
                                    mUsingSpdy);
diff --git a/netwerk/protocol/http/nsHttpConnection.h b/netwerk/protocol/http/nsHttpConnection.h
index 9bc69d47e14e..38ea00247509 100644
--- a/netwerk/protocol/http/nsHttpConnection.h
+++ b/netwerk/protocol/http/nsHttpConnection.h
@@ -180,6 +180,9 @@ private:
     // redirections
     void     HandleAlternateProtocol(nsHttpResponseHead *);
 
+    // Start the Spdy transaction handler when NPN indicates spdy/2
+    void     StartSpdy();
+
     // Directly Add a transaction to an active connection for SPDY
     nsresult AddTransaction(nsAHttpTransaction *, PRInt32);
 
diff --git a/netwerk/protocol/http/nsHttpPipeline.cpp b/netwerk/protocol/http/nsHttpPipeline.cpp
index f58f4a8026a0..6eb1908bfeee 100644
--- a/netwerk/protocol/http/nsHttpPipeline.cpp
+++ b/netwerk/protocol/http/nsHttpPipeline.cpp
@@ -364,6 +364,33 @@ nsHttpPipeline::Http1xTransactionCount()
   return mHttp1xTransactionCount;
 }
 
+nsresult
+nsHttpPipeline::TakeSubTransactions(
+    nsTArray<nsRefPtr<nsAHttpTransaction> > &outTransactions)
+{
+    LOG(("nsHttpPipeline::TakeSubTransactions [this=%p]\n", this));
+
+    if (mResponseQ.Length() || mRequestIsPartial)
+        return NS_ERROR_ALREADY_OPENED;
+
+    // request queue could be empty if it was already canceled, in which
+    // case it is safe to treat this as a case without any
+    // sub-transactions.
+    if (!mRequestQ.Length())
+        return NS_ERROR_NOT_IMPLEMENTED;
+
+    PRInt32 i, count = mRequestQ.Length();
+    for (i = 0; i < count; ++i) {
+        nsAHttpTransaction *trans = Request(i);
+        outTransactions.AppendElement(trans);
+        NS_RELEASE(trans);
+    }
+    mRequestQ.Clear();
+
+    LOG(("   took %d\n", count));
+    return NS_OK;
+}
+
 //-----------------------------------------------------------------------------
 // nsHttpPipeline::nsAHttpConnection
 //-----------------------------------------------------------------------------
diff --git a/netwerk/protocol/http/nsHttpTransaction.cpp b/netwerk/protocol/http/nsHttpTransaction.cpp
index d32c35ff2eb7..4571429d0b54 100644
--- a/netwerk/protocol/http/nsHttpTransaction.cpp
+++ b/netwerk/protocol/http/nsHttpTransaction.cpp
@@ -341,6 +341,13 @@ nsHttpTransaction::Http1xTransactionCount()
   return 1;
 }
 
+nsresult
+nsHttpTransaction::TakeSubTransactions(
+    nsTArray<nsRefPtr<nsAHttpTransaction> > &outTransactions)
+{
+    return NS_ERROR_NOT_IMPLEMENTED;
+}
+
 //----------------------------------------------------------------------------
 // nsHttpTransaction::nsAHttpTransaction
 //----------------------------------------------------------------------------
diff --git a/testing/testsuite-targets.mk b/testing/testsuite-targets.mk
index 8345d1a154d7..1bc40e6ed628 100644
--- a/testing/testsuite-targets.mk
+++ b/testing/testsuite-targets.mk
@@ -97,20 +97,24 @@ endif
 
 mochitest-remote: DM_TRANS?=adb
 mochitest-remote:
-	@if test -f ${MOZ_HOST_BIN}/xpcshell && [ "${TEST_DEVICE}" != "usb" -o "$(DM_TRANS)" = "adb" ]; \
-          then $(RUN_MOCHITEST_REMOTE); \
-        else \
-          echo "please prepare your host with environment variables for TEST_DEVICE and MOZ_HOST_BIN"; \
-        fi
+	@if [ ! -f ${MOZ_HOST_BIN}/xpcshell ]; then \
+        echo "please prepare your host with the environment variable MOZ_HOST_BIN"; \
+    elif [ "${TEST_DEVICE}" = "" -a "$(DM_TRANS)" != "adb" ]; then \
+        echo "please prepare your host with the environment variable TEST_DEVICE"; \
+    else \
+        $(RUN_MOCHITEST_REMOTE); \
+    fi
 
 mochitest-robotium: robotium-id-map
 mochitest-robotium: DM_TRANS?=adb
 mochitest-robotium:
-	@if test -f ${MOZ_HOST_BIN}/xpcshell && [ "${TEST_DEVICE}" != "usb" -o "$(DM_TRANS)" = "adb" ]; \
-          then $(RUN_MOCHITEST_ROBOTIUM); \
-        else \
-          echo "please prepare your host with environment variables for TEST_DEVICE and MOZ_HOST_BIN"; \
-        fi
+	@if [ ! -f ${MOZ_HOST_BIN}/xpcshell ]; then \
+        echo "please prepare your host with the environment variable MOZ_HOST_BIN"; \
+    elif [ "${TEST_DEVICE}" = "" -a "$(DM_TRANS)" != "adb" ]; then \
+        echo "please prepare your host with the environment variable TEST_DEVICE"; \
+    else \
+        $(RUN_MOCHITEST_ROBOTIUM); \
+    fi
 
 mochitest-plain:
 	$(RUN_MOCHITEST)
@@ -172,11 +176,15 @@ reftest:
 reftest-remote: TEST_PATH?=layout/reftests/reftest.list
 reftest-remote: DM_TRANS?=adb
 reftest-remote:
-	@if test -f ${MOZ_HOST_BIN}/xpcshell && [ "${TEST_DEVICE}" != "" -o "$(DM_TRANS)" = "adb" ]; \
-	  then ln -s $(abspath $(topsrcdir)) _tests/reftest/tests;$(call REMOTE_REFTEST,tests/$(TEST_PATH)); $(CHECK_TEST_ERROR); \
-        else \
-          echo "please prepare your host with environment variables for TEST_DEVICE and MOZ_HOST_BIN"; \
-        fi
+	@if [ ! -f ${MOZ_HOST_BIN}/xpcshell ]; then \
+        echo "please prepare your host with the environment variable MOZ_HOST_BIN"; \
+    elif [ "${TEST_DEVICE}" = "" -a "$(DM_TRANS)" != "adb" ]; then \
+        echo "please prepare your host with the environment variable TEST_DEVICE"; \
+    else \
+        ln -s $(abspath $(topsrcdir)) _tests/reftest/tests; \
+        $(call REMOTE_REFTEST,tests/$(TEST_PATH)); \
+        $(CHECK_TEST_ERROR); \
+    fi
 
 reftest-ipc: TEST_PATH?=layout/reftests/reftest.list
 reftest-ipc:
diff --git a/toolkit/components/passwordmgr/test/Makefile.in b/toolkit/components/passwordmgr/test/Makefile.in
index f8fe444e87e6..dd36ea71d500 100644
--- a/toolkit/components/passwordmgr/test/Makefile.in
+++ b/toolkit/components/passwordmgr/test/Makefile.in
@@ -76,6 +76,7 @@ MOCHI_TESTS = \
     test_bug_391514.html \
     test_bug_427033.html \
     test_bug_444968.html \
+    test_bug_627616.html \
     test_master_password.html \
     test_master_password_cleanup.html \
     test_prompt_async.html \
diff --git a/toolkit/components/passwordmgr/test/authenticate.sjs b/toolkit/components/passwordmgr/test/authenticate.sjs
index 7c2102fd0dbb..58da655cf98f 100644
--- a/toolkit/components/passwordmgr/test/authenticate.sjs
+++ b/toolkit/components/passwordmgr/test/authenticate.sjs
@@ -15,24 +15,26 @@ function reallyHandleRequest(request, response) {
 
   // 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];
 
@@ -66,6 +68,10 @@ function reallyHandleRequest(request, response) {
   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.
   //
@@ -74,8 +80,9 @@ function reallyHandleRequest(request, response) {
   // 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)
@@ -115,16 +122,24 @@ function reallyHandleRequest(request, response) {
     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);
diff --git a/toolkit/components/passwordmgr/test/prompt_common.js b/toolkit/components/passwordmgr/test/prompt_common.js
index c93d3b131f3f..a25ffd17cd16 100644
--- a/toolkit/components/passwordmgr/test/prompt_common.js
+++ b/toolkit/components/passwordmgr/test/prompt_common.js
@@ -1,8 +1,8 @@
 netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
 
-const Ci = Components.interfaces;
+var Ci = Components.interfaces;
 ok(Ci != null, "Access Ci");
-const Cc = Components.classes;
+var Cc = Components.classes;
 ok(Cc != null, "Access Cc");
 
 var didDialog;
diff --git a/toolkit/components/passwordmgr/test/test_bug_627616.html b/toolkit/components/passwordmgr/test/test_bug_627616.html
new file mode 100644
index 000000000000..e4f5f0892f5b
--- /dev/null
+++ b/toolkit/components/passwordmgr/test/test_bug_627616.html
@@ -0,0 +1,106 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test bug 627616</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>        
+  <script type="text/javascript" src="prompt_common.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<script class="testbody" type="text/javascript">
+    SimpleTest.waitForExplicitFinish();
+
+    var Cc = SpecialPowers.wrap(Components).classes;
+
+    testNum = 1;
+
+    var login, login2;
+    function init() {
+        var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
+        var pps = Cc["@mozilla.org/network/protocol-proxy-service;1"].getService();
+
+        var uri = ios.newURI("http://example.com", null, null);
+        var pi = pps.resolve(uri, 0);
+        var mozproxy = "moz-proxy://" + pi.host + ":" + pi.port;
+
+        var pwmgr = Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager);
+        login = Cc["@mozilla.org/login-manager/loginInfo;1"].createInstance(Ci.nsILoginInfo);
+        login.init(mozproxy, null, "proxy_realm", "proxy_user", "proxy_pass", "", "");
+        pwmgr.addLogin(login);
+
+        login2 = Cc["@mozilla.org/login-manager/loginInfo;1"].createInstance(Ci.nsILoginInfo);
+        login2.init("http://mochi.test:8888", null, "mochirealm", "user1name", "user1pass", "", "");
+        pwmgr.addLogin(login2);
+    }
+    function cleanup() {
+        var pwmgr = Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager);
+        pwmgr.removeLogin(login);        
+        pwmgr.removeLogin(login2);
+    }
+
+    function makeXHR(expectedStatus, expectedText, extra) {
+      var xhr =  new XMLHttpRequest();
+      xhr.open("GET", "authenticate.sjs?" +
+                      "proxy_user=proxy_user&" +
+                      "proxy_pass=proxy_pass&" +
+                      "proxy_realm=proxy_realm&" +
+                      "user=user1name&" +
+                      "pass=user1pass&" +
+                      "realm=mochirealm&" +
+                      extra || "");
+      xhr.onloadend = function() {
+        is(xhr.status, expectedStatus);
+        is(xhr.statusText, expectedText);
+        runNextTest();
+      }
+      return xhr;
+    }
+
+    function testNonAnonymousCredentials() {
+      var xhr = makeXHR(200, "OK");
+      xhr.send();
+      startCallbackTimer();
+    }
+
+    function testAnonymousCredentials() {
+      // Test that an anonymous request correctly performs proxy authentication
+      var xhr = makeXHR(401, "Authentication required");
+      SpecialPowers.wrap(xhr).channel.loadFlags |= Ci.nsIChannel.LOAD_ANONYMOUS;
+      xhr.send();
+    }
+
+    function testAnonymousNoAuth() {
+      // Next, test that an anonymous request still does not include any non-proxy
+      // authentication headers.
+      var xhr = makeXHR(200, "Authorization header not found", "anonymous=1");
+      SpecialPowers.wrap(xhr).channel.loadFlags |= Ci.nsIChannel.LOAD_ANONYMOUS;
+      xhr.send();
+    }
+
+    var gCurrentTest;
+    function runNextTest() {
+      if (pendingTests.length > 0) {
+        gCurrentTest = pendingTests.shift();
+        gCurrentTest.call(this);
+      } else {
+        cleanup();
+        SimpleTest.finish();
+      }
+    }
+
+    var pendingTests = [testNonAnonymousCredentials, testAnonymousCredentials,
+                        testAnonymousNoAuth];
+    init();
+    runNextTest();
+
+    function handleDialog(doc, testNum)
+    {
+        var dialog = doc.getElementById("commonDialog");
+        dialog.acceptDialog();
+        if (gCurrentTest == testNonAnonymousCredentials) {
+          startCallbackTimer();
+        }
+    }
+</script>
+</body>
+</html>
diff --git a/toolkit/components/places/tests/expiration/test_annos_expire_session.js b/toolkit/components/places/tests/expiration/test_annos_expire_session.js
index 17f2763eb449..5a1e7aeb6de2 100644
--- a/toolkit/components/places/tests/expiration/test_annos_expire_session.js
+++ b/toolkit/components/places/tests/expiration/test_annos_expire_session.js
@@ -86,17 +86,28 @@ function run_test() {
   items = as.getItemsWithAnnotation("test2");
   do_check_eq(items.length, 10);
 
-  waitForAsyncUpdates(function() {
-    let stmt = DBConn().createStatement(
-      "SELECT id FROM moz_annos "
-    + "UNION "
-    + "SELECT id FROM moz_items_annos "
-    );
-    do_check_false(stmt.executeStep());
-    stmt.finalize();
-
-    do_test_finished();
-  });
-
   shutdownPlaces();
+
+  let stmt = DBConn(true).createAsyncStatement(
+    "SELECT id FROM moz_annos "
+  + "UNION "
+  + "SELECT id FROM moz_items_annos "
+  );
+  stmt.executeAsync({
+    handleResult: function(aResultSet)
+    {
+      do_throw("Should not find any leftover session annotations");
+    },
+    handleError: function(aError)
+    {
+      do_throw("Error code " + aError.result + " with message '" +
+               aError.message + "' returned.");
+    },
+    handleCompletion: function(aReason)
+    {
+      do_check_eq(aReason, Ci.mozIStorageStatementCallback.REASON_FINISHED);
+      do_test_finished();
+    }
+  });
+  stmt.finalize();
 }
diff --git a/toolkit/components/places/tests/head_common.js b/toolkit/components/places/tests/head_common.js
index dc585d0b2dcb..5a0af5be5bf2 100644
--- a/toolkit/components/places/tests/head_common.js
+++ b/toolkit/components/places/tests/head_common.js
@@ -112,25 +112,32 @@ function uri(aSpec) NetUtil.newURI(aSpec);
  * Gets the database connection.  If the Places connection is invalid it will
  * try to create a new connection.
  *
+ * @param [optional] aForceNewConnection
+ *        Forces creation of a new connection to the database.  When a
+ *        connection is asyncClosed it cannot anymore schedule async statements,
+ *        though connectionReady will keep returning true (Bug 726990).
+ *
  * @return The database connection or null if unable to get one.
  */
 let gDBConn;
-function DBConn() {
-  let db = PlacesUtils.history.QueryInterface(Ci.nsPIPlacesDatabase)
-                              .DBConnection;
-  if (db.connectionReady)
-    return db;
+function DBConn(aForceNewConnection) {
+  if (!aForceNewConnection) {
+    let db = PlacesUtils.history.QueryInterface(Ci.nsPIPlacesDatabase)
+                                .DBConnection;
+    if (db.connectionReady)
+      return db;
+  }
 
   // If the Places database connection has been closed, create a new connection.
-  if (!gDBConn) {
+  if (!gDBConn || aForceNewConnection) {
     let file = Services.dirsvc.get('ProfD', Ci.nsIFile);
     file.append("places.sqlite");
-    gDBConn = Services.storage.openDatabase(file);
+    let dbConn = gDBConn = Services.storage.openDatabase(file);
 
     // Be sure to cleanly close this connection.
-    Services.obs.addObserver(function (aSubject, aTopic, aData) {
-      Services.obs.removeObserver(arguments.callee, aTopic);
-      gDBConn.asyncClose();
+    Services.obs.addObserver(function DBCloseCallback(aSubject, aTopic, aData) {
+      Services.obs.removeObserver(DBCloseCallback, aTopic);
+      dbConn.asyncClose();
     }, "profile-before-change", false);
   }
 
diff --git a/toolkit/components/places/tests/inline/head_autocomplete.js b/toolkit/components/places/tests/inline/head_autocomplete.js
index 6846920af8db..d475468a0ae6 100644
--- a/toolkit/components/places/tests/inline/head_autocomplete.js
+++ b/toolkit/components/places/tests/inline/head_autocomplete.js
@@ -21,9 +21,32 @@ XPCOMUtils.defineLazyServiceGetter(this, "gHistory",
                                    "@mozilla.org/browser/history;1",
                                    "mozIAsyncHistory");
 
+function VisitInfo(aTransitionType, aVisitTime)
+{
+  this.transitionType =
+    aTransitionType === undefined ? TRANSITION_LINK : aTransitionType;
+  this.visitDate = aVisitTime || Date.now() * 1000;
+}
+
+function addVisits(aUrls)
+{
+  let places = [];
+  aUrls.forEach(function(url) {
+    places.push({
+                  uri: url.url,
+                  title: "test for " + url.url,
+                  visits: [
+                    new VisitInfo(url.transition),
+                  ],
+    });
+  });
+
+  gHistory.updatePlaces(places);
+}
+
 /**
  * @param aSearches
- *        Array of AutoCompleteSearch names. 
+ *        Array of AutoCompleteSearch names.
  */
 function AutoCompleteInput(aSearches) {
   this.searches = aSearches;
diff --git a/toolkit/components/places/tests/inline/test_autocomplete_functional.js b/toolkit/components/places/tests/inline/test_autocomplete_functional.js
new file mode 100644
index 000000000000..6836e3024e98
--- /dev/null
+++ b/toolkit/components/places/tests/inline/test_autocomplete_functional.js
@@ -0,0 +1,148 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Functional tests for inline autocomplete
+
+add_autocomplete_test([
+  "Add urls, check for correct order",
+  "vis",
+  "visit2.mozilla.org/",
+  function ()
+  {
+    let urls = [{ url: NetUtil.newURI("http://visit1.mozilla.org"),
+                  transition: undefined,
+                },
+                { url: NetUtil.newURI("http://visit2.mozilla.org"),
+                  transition: TRANSITION_TYPED,
+                }];
+    addVisits(urls);
+  }
+]);
+
+add_autocomplete_test([
+  "Add urls, make sure www and http are ignored",
+  "visit1",
+  "visit1.mozilla.org/",
+  function ()
+  {
+
+    let urls = [{ url: NetUtil.newURI("http://www.visit1.mozilla.org"),
+                  transition: undefined,
+                },
+               ];
+    addVisits(urls);
+  }
+]);
+
+add_autocomplete_test([
+  "Autocompleting after an existing host completes to the url",
+  "visit3.mozilla.org/",
+  "visit3.mozilla.org/",
+  function ()
+  {
+
+    let urls = [{ url: NetUtil.newURI("http://www.visit3.mozilla.org"),
+                  transition: undefined,
+                },
+               ];
+    addVisits(urls);
+  }
+]);
+
+add_autocomplete_test([
+  "Searching for www.me should yield www.me.mozilla.org/",
+  "www.me",
+  "www.me.mozilla.org/",
+  function ()
+  {
+
+    let urls = [{ url: NetUtil.newURI("http://www.me.mozilla.org"),
+                  transition: undefined,
+                },
+               ];
+    addVisits(urls);
+  }
+]);
+
+add_autocomplete_test([
+  "With a bookmark and history, the query result should be the bookmark",
+  "bookmark",
+  "bookmark1.mozilla.org/",
+  function ()
+  {
+
+    let urls = [{ url: NetUtil.newURI("http://bookmark1.mozilla.org/foo"),
+                  transition: undefined,
+                },
+               ];
+    addBookmark({ url: "http://bookmark1.mozilla.org/", });
+    addVisits(urls);
+  }
+]);
+
+add_autocomplete_test([
+  "Check to make sure we get the proper results with full paths",
+  "smokey",
+  "smokey.mozilla.org/",
+  function ()
+  {
+
+    let urls = [{ url: NetUtil.newURI("http://smokey.mozilla.org/foo/bar/baz?bacon=delicious"),
+                  transition: undefined,
+                },
+                { url: NetUtil.newURI("http://smokey.mozilla.org/foo/bar/baz?bacon=smokey"),
+                  transition: undefined,
+                },
+               ];
+    addVisits(urls);
+  }
+]);
+
+add_autocomplete_test([
+  "Check to make sure we autocomplete to the following '/'",
+  "smokey.mozilla.org/fo",
+  "smokey.mozilla.org/foo/",
+  function ()
+  {
+
+    let urls = [{ url: NetUtil.newURI("http://smokey.mozilla.org/foo/bar/baz?bacon=delicious"),
+                  transition: undefined,
+                },
+                { url: NetUtil.newURI("http://smokey.mozilla.org/foo/bar/baz?bacon=smokey"),
+                  transition: undefined,
+                },
+               ];
+    addVisits(urls);
+  }
+]);
+
+add_autocomplete_test([
+  "Check to make sure we autocomplete after ?",
+  "smokey.mozilla.org/foo?",
+  "smokey.mozilla.org/foo?bacon=delicious",
+  function ()
+  {
+
+    let urls = [{ url: NetUtil.newURI("http://smokey.mozilla.org/foo?bacon=delicious"),
+                  transition: undefined,
+                },
+               ];
+    addVisits(urls);
+  }
+]);
+
+add_autocomplete_test([
+  "Check to make sure we autocomplete after #",
+  "smokey.mozilla.org/foo?bacon=delicious#bar",
+  "smokey.mozilla.org/foo?bacon=delicious#bar",
+  function ()
+  {
+
+    let urls = [{ url: NetUtil.newURI("http://smokey.mozilla.org/foo?bacon=delicious#bar"),
+                  transition: undefined,
+                },
+               ];
+    addVisits(urls);
+  }
+]);
diff --git a/toolkit/components/places/tests/inline/xpcshell.ini b/toolkit/components/places/tests/inline/xpcshell.ini
index 41a0312c6e09..2a4d0089541a 100644
--- a/toolkit/components/places/tests/inline/xpcshell.ini
+++ b/toolkit/components/places/tests/inline/xpcshell.ini
@@ -2,5 +2,6 @@
 head = head_autocomplete.js
 tail = 
 
+[test_autocomplete_functional.js]
 [test_casing.js]
 [test_keywords.js]
diff --git a/toolkit/content/widgets/videocontrols.css b/toolkit/content/widgets/videocontrols.css
index ec38d974b6b0..ab9975701b6e 100644
--- a/toolkit/content/widgets/videocontrols.css
+++ b/toolkit/content/widgets/videocontrols.css
@@ -27,7 +27,7 @@
 }
 
 .controlsSpacer[hideCursor] {
- cursor: none;
+  cursor: none;
 }
 
 /* CSS Transitions
diff --git a/toolkit/library/Makefile.in b/toolkit/library/Makefile.in
index 2b8e9fd85962..8162e70ea212 100644
--- a/toolkit/library/Makefile.in
+++ b/toolkit/library/Makefile.in
@@ -398,6 +398,9 @@ OS_LIBS += \
   -lbinder \
   -lsensorservice \
   $(NULL)
+ifdef MOZ_B2G_BT
+OS_LIBS += -lbluedroid
+endif
 endif
 
 EXTRA_DEPS += \
diff --git a/toolkit/mozapps/installer/packager.mk b/toolkit/mozapps/installer/packager.mk
index 1b3b37fa7a0d..61b5a868297a 100644
--- a/toolkit/mozapps/installer/packager.mk
+++ b/toolkit/mozapps/installer/packager.mk
@@ -342,7 +342,7 @@ UPLOAD_EXTRA_FILES += robocop.apk
 UPLOAD_EXTRA_FILES += fennec_ids.txt
 ROBOCOP_PATH = $(call core_abspath,$(_ABS_DIST)/../build/mobile/robocop)
 INNER_ROBOCOP_PACKAGE= \
-  $(PYTHON) $(topsrcdir)/build/mobile/robocop/parse_ids.py -i $(call core_abspath,$(DEPTH)/mobile/android/base/R.java) -o $(call core_abspath,$(DEPTH)/build/mobile/robocop/fennec_ids.txt) && \
+  $(PYTHON) $(abspath $(topsrcdir)/build/mobile/robocop/parse_ids.py) -i $(call core_abspath,$(DEPTH)/mobile/android/base/R.java) -o $(call core_abspath,$(DEPTH)/build/mobile/robocop/fennec_ids.txt) && \
   $(NSINSTALL) $(call core_abspath,$(DEPTH)/build/mobile/robocop/fennec_ids.txt) $(_ABS_DIST) && \
   $(APKBUILDER) $(_ABS_DIST)/robocop-raw.apk -v $(APKBUILDER_FLAGS) -z $(ROBOCOP_PATH)/robocop.ap_ -f $(ROBOCOP_PATH)/classes.dex && \
   $(JARSIGNER) $(_ABS_DIST)/robocop-raw.apk && \
diff --git a/toolkit/mozapps/update/common/updatelogging.cpp b/toolkit/mozapps/update/common/updatelogging.cpp
index 051a0840a86e..285e6730dd70 100644
--- a/toolkit/mozapps/update/common/updatelogging.cpp
+++ b/toolkit/mozapps/update/common/updatelogging.cpp
@@ -51,7 +51,7 @@ UpdateLog::UpdateLog() : logFP(NULL)
 {
 }
 
-void UpdateLog::Init(NS_tchar* sourcePath, NS_tchar* fileName)
+void UpdateLog::Init(NS_tchar* sourcePath, const NS_tchar* fileName)
 {
   if (logFP)
     return;
diff --git a/toolkit/mozapps/update/common/updatelogging.h b/toolkit/mozapps/update/common/updatelogging.h
index e53f9e6bc2e7..9d0b4fb999ee 100644
--- a/toolkit/mozapps/update/common/updatelogging.h
+++ b/toolkit/mozapps/update/common/updatelogging.h
@@ -50,7 +50,7 @@ public:
     return primaryLog;
   }
 
-  void Init(NS_tchar* sourcePath, NS_tchar* fileName);
+  void Init(NS_tchar* sourcePath, const NS_tchar* fileName);
   void Finish();
   void Flush();
   void Printf(const char *fmt, ... );
diff --git a/toolkit/mozapps/update/updater/updater.cpp b/toolkit/mozapps/update/updater/updater.cpp
index 68e6bcd27e6d..50c22537e2a0 100644
--- a/toolkit/mozapps/update/updater/updater.cpp
+++ b/toolkit/mozapps/update/updater/updater.cpp
@@ -1452,6 +1452,7 @@ WriteStatusApplying()
   return true;
 }
 
+#ifdef MOZ_MAINTENANCE_SERVICE
 /* 
  * Read the update.status file and sets isPendingService to true if
  * the status is set to pending-service.
@@ -1486,7 +1487,9 @@ IsUpdateStatusPending(bool &isPendingService)
                              sizeof(kPendingService) - 1) == 0;
   return isPending;
 }
+#endif
 
+#ifdef XP_WIN
 /* 
  * Read the update.status file and sets isSuccess to true if
  * the status is set to succeeded.
@@ -1516,7 +1519,6 @@ IsUpdateStatusSucceeded(bool &isSucceeded)
   return true;
 }
 
-#ifdef XP_WIN
 static void 
 WaitForServiceFinishThread(void *param)
 {
@@ -2555,7 +2557,6 @@ int DoUpdate()
   ActionList list;
   NS_tchar *line;
   bool isFirstAction = true;
-  bool isComplete = false;
 
   while((line = mstrtok(kNL, &rb)) != 0) {
     // skip comments
@@ -2572,7 +2573,6 @@ int DoUpdate()
       const NS_tchar *type = mstrtok(kQuote, &line);
       LOG(("UPDATE TYPE " LOG_S "\n", type));
       if (NS_tstrcmp(type, NS_T("complete")) == 0) {
-        isComplete = true;
         rv = AddPreCompleteActions(&list);
         if (rv)
           return rv;
diff --git a/widget/android/AndroidJavaWrappers.cpp b/widget/android/AndroidJavaWrappers.cpp
index 3fcadd2cf945..2051fe78c09f 100644
--- a/widget/android/AndroidJavaWrappers.cpp
+++ b/widget/android/AndroidJavaWrappers.cpp
@@ -547,6 +547,14 @@ AndroidGeckoEvent::Init(JNIEnv *jenv, jobject jobj)
             break;
         }
 
+        case ACTIVITY_STOPPING:
+        case ACTIVITY_START:
+        case ACTIVITY_PAUSING:
+        case ACTIVITY_RESUMING: {
+            mFlags = jenv->GetIntField(jobj, jFlagsField);
+            break;
+        }
+
         default:
             break;
     }
diff --git a/widget/android/AndroidJavaWrappers.h b/widget/android/AndroidJavaWrappers.h
index 751c5be6e031..de02c6a40dbd 100644
--- a/widget/android/AndroidJavaWrappers.h
+++ b/widget/android/AndroidJavaWrappers.h
@@ -569,6 +569,7 @@ public:
         VISITED = 21,
         NETWORK_CHANGED = 22,
         PROXIMITY_EVENT = 23,
+        ACTIVITY_RESUMING = 24,
         dummy_java_enum_list_end
     };
 
diff --git a/widget/android/AndroidMediaLayer.cpp b/widget/android/AndroidMediaLayer.cpp
index 572dadcc46b0..d5691c3c29f5 100644
--- a/widget/android/AndroidMediaLayer.cpp
+++ b/widget/android/AndroidMediaLayer.cpp
@@ -45,7 +45,7 @@
 namespace mozilla {
 
 AndroidMediaLayer::AndroidMediaLayer()
-  : mInverted(false) {
+  : mInverted(false), mVisible(true) {
 }
 
 AndroidMediaLayer::~AndroidMediaLayer() {
@@ -132,6 +132,8 @@ void AndroidMediaLayer::SetNativeWindowDimensions(void* aWindow, const gfxRect&
 }
 
 void AndroidMediaLayer::UpdatePosition(const gfxRect& aRect, float aZoomLevel) {
+  if (!mVisible)
+    return;
 
   std::map<void*, SurfaceData*>::iterator it;
 
@@ -152,4 +154,24 @@ void AndroidMediaLayer::UpdatePosition(const gfxRect& aRect, float aZoomLevel) {
   }
 }
 
+void AndroidMediaLayer::SetVisible(bool aVisible) {
+  if (aVisible == mVisible)
+    return;
+
+  mVisible = aVisible;
+  if (mVisible)
+    return;
+
+  // Hide all surfaces
+  std::map<void*, SurfaceData*>::iterator it;
+
+  if (EnsureContentSurface())
+    AndroidBridge::Bridge()->HideSurface(mContentData.surface);
+
+  for (it = mVideoSurfaces.begin(); it != mVideoSurfaces.end(); it++) {
+    SurfaceData* data = it->second;
+    AndroidBridge::Bridge()->HideSurface(data->surface);
+  }
+}
+
 } /* mozilla */
diff --git a/widget/android/AndroidMediaLayer.h b/widget/android/AndroidMediaLayer.h
index eb402743fc17..a5b01e6e080d 100644
--- a/widget/android/AndroidMediaLayer.h
+++ b/widget/android/AndroidMediaLayer.h
@@ -41,15 +41,17 @@
 #include <map>
 #include <jni.h>
 #include "gfxRect.h"
+#include "nsISupports.h"
 
 namespace mozilla {
 
 class AndroidMediaLayer
 {
 public:
-
   AndroidMediaLayer();
   virtual ~AndroidMediaLayer();
+
+  NS_INLINE_DECL_REFCOUNTING(AndroidMediaLayer)
   
   void* GetNativeWindowForContent();
 
@@ -68,8 +70,15 @@ public:
     mInverted = aInverted;
   }
 
+  bool IsVisible() {
+    return mVisible;
+  }
+
+  void SetVisible(bool aVisible);
+
 private:
   bool mInverted;
+  bool mVisible;
 
   class SurfaceData {
     public:
diff --git a/widget/android/nsAppShell.cpp b/widget/android/nsAppShell.cpp
index 581c21b9272f..e7e570ef7eab 100644
--- a/widget/android/nsAppShell.cpp
+++ b/widget/android/nsAppShell.cpp
@@ -370,6 +370,9 @@ nsAppShell::ProcessNextNativeEvent(bool mayWait)
     }
 
     case AndroidGeckoEvent::ACTIVITY_STOPPING: {
+        if (curEvent->Flags() > 0)
+            break;
+
         nsCOMPtr<nsIObserverService> obsServ =
             mozilla::services::GetObserverService();
         NS_NAMED_LITERAL_STRING(minimize, "heap-minimize");
@@ -395,6 +398,14 @@ nsAppShell::ProcessNextNativeEvent(bool mayWait)
     }
 
     case AndroidGeckoEvent::ACTIVITY_PAUSING: {
+        if (curEvent->Flags() == 0) {
+            // We aren't transferring to one of our own activities, so set
+            // background status
+            nsCOMPtr<nsIObserverService> obsServ =
+                mozilla::services::GetObserverService();
+            obsServ->NotifyObservers(nsnull, "application-background", nsnull);
+        }
+
         // We really want to send a notification like profile-before-change,
         // but profile-before-change ends up shutting some things down instead
         // of flushing data
@@ -413,6 +424,9 @@ nsAppShell::ProcessNextNativeEvent(bool mayWait)
     }
 
     case AndroidGeckoEvent::ACTIVITY_START: {
+        if (curEvent->Flags() > 0)
+            break;
+
         nsCOMPtr<nsIObserverService> obsServ =
             mozilla::services::GetObserverService();
         obsServ->NotifyObservers(nsnull, "application-foreground", nsnull);
@@ -483,6 +497,17 @@ nsAppShell::ProcessNextNativeEvent(bool mayWait)
         break;
     }
 
+    case AndroidGeckoEvent::ACTIVITY_RESUMING: {
+        if (curEvent->Flags() == 0) {
+            // We didn't return from one of our own activities, so restore
+            // to foreground status
+            nsCOMPtr<nsIObserverService> obsServ =
+                mozilla::services::GetObserverService();
+            obsServ->NotifyObservers(nsnull, "application-foreground", nsnull);
+        }
+        break;
+    }
+
     default:
         nsWindow::OnGlobalAndroidEvent(curEvent);
     }
diff --git a/widget/cocoa/nsMenuItemX.h b/widget/cocoa/nsMenuItemX.h
index f858c16780cb..0cff11e4e92e 100644
--- a/widget/cocoa/nsMenuItemX.h
+++ b/widget/cocoa/nsMenuItemX.h
@@ -90,6 +90,8 @@ public:
   nsresult      DispatchDOMEvent(const nsString &eventName, bool* preventDefaultCalled);
   void          SetupIcon();
 
+  static PRUint32   ConvertGeckoToMacKeyCode(nsAString& aKeyCodeName);
+
 protected:
   void UncheckRadioSiblings(nsIContent* inCheckedElement);
   void SetKeyEquiv();
diff --git a/widget/cocoa/nsMenuItemX.mm b/widget/cocoa/nsMenuItemX.mm
index 0b8e5fc6043d..f1328aeee760 100644
--- a/widget/cocoa/nsMenuItemX.mm
+++ b/widget/cocoa/nsMenuItemX.mm
@@ -89,6 +89,159 @@ nsMenuItemX::~nsMenuItemX()
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
+struct macKeyCodeData {
+  const char* str;
+  size_t strlength;
+  PRUint32 keycode;
+};
+
+static const macKeyCodeData gMacKeyCodes[] = {
+
+#define KEYCODE_ENTRY(str, code) {#str, sizeof(#str) - 1, code}
+
+  KEYCODE_ENTRY(VK_CANCEL, 0x001B),
+  KEYCODE_ENTRY(VK_DELETE, NSBackspaceCharacter),
+  KEYCODE_ENTRY(VK_BACK, NSBackspaceCharacter),
+  KEYCODE_ENTRY(VK_BACK_SPACE, NSBackspaceCharacter),
+  KEYCODE_ENTRY(VK_TAB, NSTabCharacter),
+  KEYCODE_ENTRY(VK_CLEAR, NSClearLineFunctionKey),
+  KEYCODE_ENTRY(VK_RETURN, NSEnterCharacter),
+  KEYCODE_ENTRY(VK_ENTER, NSEnterCharacter),
+  KEYCODE_ENTRY(VK_SHIFT, 0),
+  KEYCODE_ENTRY(VK_CONTROL, 0),
+  KEYCODE_ENTRY(VK_ALT, 0),
+  KEYCODE_ENTRY(VK_PAUSE, NSPauseFunctionKey),
+  KEYCODE_ENTRY(VK_CAPS_LOCK, 0),
+  KEYCODE_ENTRY(VK_ESCAPE, 0),
+  KEYCODE_ENTRY(VK_SPACE, ' '),
+  KEYCODE_ENTRY(VK_PAGE_UP, NSPageUpFunctionKey),
+  KEYCODE_ENTRY(VK_PAGE_DOWN, NSPageDownFunctionKey),
+  KEYCODE_ENTRY(VK_END, NSEndFunctionKey),
+  KEYCODE_ENTRY(VK_HOME, NSHomeFunctionKey),
+  KEYCODE_ENTRY(VK_LEFT, NSLeftArrowFunctionKey),
+  KEYCODE_ENTRY(VK_UP, NSUpArrowFunctionKey),
+  KEYCODE_ENTRY(VK_RIGHT, NSRightArrowFunctionKey),
+  KEYCODE_ENTRY(VK_DOWN, NSDownArrowFunctionKey),
+  KEYCODE_ENTRY(VK_PRINTSCREEN, NSPrintScreenFunctionKey),
+  KEYCODE_ENTRY(VK_INSERT, NSInsertFunctionKey),
+  KEYCODE_ENTRY(VK_HELP, NSHelpFunctionKey),
+  KEYCODE_ENTRY(VK_0, '0'),
+  KEYCODE_ENTRY(VK_1, '1'),
+  KEYCODE_ENTRY(VK_2, '2'),
+  KEYCODE_ENTRY(VK_3, '3'),
+  KEYCODE_ENTRY(VK_4, '4'),
+  KEYCODE_ENTRY(VK_5, '5'),
+  KEYCODE_ENTRY(VK_6, '6'),
+  KEYCODE_ENTRY(VK_7, '7'),
+  KEYCODE_ENTRY(VK_8, '8'),
+  KEYCODE_ENTRY(VK_9, '9'),
+  KEYCODE_ENTRY(VK_SEMICOLON, ':'),
+  KEYCODE_ENTRY(VK_EQUALS, '='),
+  KEYCODE_ENTRY(VK_A, 'A'),
+  KEYCODE_ENTRY(VK_B, 'B'),
+  KEYCODE_ENTRY(VK_C, 'C'),
+  KEYCODE_ENTRY(VK_D, 'D'),
+  KEYCODE_ENTRY(VK_E, 'E'),
+  KEYCODE_ENTRY(VK_F, 'F'),
+  KEYCODE_ENTRY(VK_G, 'G'),
+  KEYCODE_ENTRY(VK_H, 'H'),
+  KEYCODE_ENTRY(VK_I, 'I'),
+  KEYCODE_ENTRY(VK_J, 'J'),
+  KEYCODE_ENTRY(VK_K, 'K'),
+  KEYCODE_ENTRY(VK_L, 'L'),
+  KEYCODE_ENTRY(VK_M, 'M'),
+  KEYCODE_ENTRY(VK_N, 'N'),
+  KEYCODE_ENTRY(VK_O, 'O'),
+  KEYCODE_ENTRY(VK_P, 'P'),
+  KEYCODE_ENTRY(VK_Q, 'Q'),
+  KEYCODE_ENTRY(VK_R, 'R'),
+  KEYCODE_ENTRY(VK_S, 'S'),
+  KEYCODE_ENTRY(VK_T, 'T'),
+  KEYCODE_ENTRY(VK_U, 'U'),
+  KEYCODE_ENTRY(VK_V, 'V'),
+  KEYCODE_ENTRY(VK_W, 'W'),
+  KEYCODE_ENTRY(VK_X, 'X'),
+  KEYCODE_ENTRY(VK_Y, 'Y'),
+  KEYCODE_ENTRY(VK_Z, 'Z'),
+  KEYCODE_ENTRY(VK_CONTEXT_MENU, NSMenuFunctionKey),
+  KEYCODE_ENTRY(VK_NUMPAD0, '0'),
+  KEYCODE_ENTRY(VK_NUMPAD1, '1'),
+  KEYCODE_ENTRY(VK_NUMPAD2, '2'),
+  KEYCODE_ENTRY(VK_NUMPAD3, '3'),
+  KEYCODE_ENTRY(VK_NUMPAD4, '4'),
+  KEYCODE_ENTRY(VK_NUMPAD5, '5'),
+  KEYCODE_ENTRY(VK_NUMPAD6, '6'),
+  KEYCODE_ENTRY(VK_NUMPAD7, '7'),
+  KEYCODE_ENTRY(VK_NUMPAD8, '8'),
+  KEYCODE_ENTRY(VK_NUMPAD9, '9'),
+  KEYCODE_ENTRY(VK_MULTIPLY, '*'),
+  KEYCODE_ENTRY(VK_ADD, '+'),
+  KEYCODE_ENTRY(VK_SEPARATOR, 0),
+  KEYCODE_ENTRY(VK_SUBTRACT, '-'),
+  KEYCODE_ENTRY(VK_DECIMAL, '.'),
+  KEYCODE_ENTRY(VK_DIVIDE, '/'),
+  KEYCODE_ENTRY(VK_F1, NSF1FunctionKey),
+  KEYCODE_ENTRY(VK_F2, NSF2FunctionKey),
+  KEYCODE_ENTRY(VK_F3, NSF3FunctionKey),
+  KEYCODE_ENTRY(VK_F4, NSF4FunctionKey),
+  KEYCODE_ENTRY(VK_F5, NSF5FunctionKey),
+  KEYCODE_ENTRY(VK_F6, NSF6FunctionKey),
+  KEYCODE_ENTRY(VK_F7, NSF7FunctionKey),
+  KEYCODE_ENTRY(VK_F8, NSF8FunctionKey),
+  KEYCODE_ENTRY(VK_F9, NSF9FunctionKey),
+  KEYCODE_ENTRY(VK_F10, NSF10FunctionKey),
+  KEYCODE_ENTRY(VK_F11, NSF11FunctionKey),
+  KEYCODE_ENTRY(VK_F12, NSF12FunctionKey),
+  KEYCODE_ENTRY(VK_F13, NSF13FunctionKey),
+  KEYCODE_ENTRY(VK_F14, NSF14FunctionKey),
+  KEYCODE_ENTRY(VK_F15, NSF15FunctionKey),
+  KEYCODE_ENTRY(VK_F16, NSF16FunctionKey),
+  KEYCODE_ENTRY(VK_F17, NSF17FunctionKey),
+  KEYCODE_ENTRY(VK_F18, NSF18FunctionKey),
+  KEYCODE_ENTRY(VK_F19, NSF19FunctionKey),
+  KEYCODE_ENTRY(VK_F20, NSF20FunctionKey),
+  KEYCODE_ENTRY(VK_F21, NSF21FunctionKey),
+  KEYCODE_ENTRY(VK_F22, NSF22FunctionKey),
+  KEYCODE_ENTRY(VK_F23, NSF23FunctionKey),
+  KEYCODE_ENTRY(VK_F24, NSF24FunctionKey),
+  KEYCODE_ENTRY(VK_NUM_LOCK, NSClearLineFunctionKey),
+  KEYCODE_ENTRY(VK_SCROLL_LOCK, NSScrollLockFunctionKey),
+  KEYCODE_ENTRY(VK_COMMA, ','),
+  KEYCODE_ENTRY(VK_PERIOD, '.'),
+  KEYCODE_ENTRY(VK_SLASH, '/'),
+  KEYCODE_ENTRY(VK_BACK_QUOTE, '`'),
+  KEYCODE_ENTRY(VK_OPEN_BRACKET, '['),
+  KEYCODE_ENTRY(VK_BACK_SLASH, '\\'),
+  KEYCODE_ENTRY(VK_CLOSE_BRACKET, ']'),
+  KEYCODE_ENTRY(VK_QUOTE, '\'')
+
+#undef KEYCODE_ENTRY
+
+};
+
+PRUint32 nsMenuItemX::ConvertGeckoToMacKeyCode(nsAString& aKeyCodeName)
+{
+  if (aKeyCodeName.IsEmpty()) {
+    return 0;
+  }
+
+  nsCAutoString keyCodeName;
+  keyCodeName.AssignWithConversion(aKeyCodeName);
+  // We want case-insensitive comparison with data stored as uppercase.
+  ToUpperCase(keyCodeName);
+
+  PRUint32 keyCodeNameLength = keyCodeName.Length();
+  const char* keyCodeNameStr = keyCodeName.get();
+  for (PRUint16 i = 0; i < (sizeof(gMacKeyCodes) / sizeof(gMacKeyCodes[0])); ++i) {
+    if (keyCodeNameLength == gMacKeyCodes[i].strlength &&
+        nsCRT::strcmp(gMacKeyCodes[i].str, keyCodeNameStr) == 0) {
+      return gMacKeyCodes[i].keycode;
+    }
+  }
+
+  return 0;
+}
+
 nsresult nsMenuItemX::Create(nsMenuX* aParent, const nsString& aLabel, EMenuItemType aItemType,
                              nsMenuGroupOwnerX* aMenuGroupOwner, nsIContent* aNode)
 {
@@ -274,8 +427,20 @@ void nsMenuItemX::SetKeyEquiv()
   if (!keyValue.IsEmpty() && mContent->GetCurrentDoc()) {
     nsIContent *keyContent = mContent->GetCurrentDoc()->GetElementById(keyValue);
     if (keyContent) {
-      nsAutoString keyChar(NS_LITERAL_STRING(" "));
-      keyContent->GetAttr(kNameSpaceID_None, nsGkAtoms::key, keyChar);
+      nsAutoString keyChar;
+      bool hasKey = keyContent->GetAttr(kNameSpaceID_None, nsGkAtoms::key, keyChar);
+
+      if (!hasKey || keyChar.IsEmpty()) {
+        nsAutoString keyCodeName;
+        keyContent->GetAttr(kNameSpaceID_None, nsGkAtoms::keycode, keyCodeName);
+        PRUint32 keycode = ConvertGeckoToMacKeyCode(keyCodeName);
+        if (keycode) {
+          keyChar.Assign(keycode);
+        }
+        else {
+          keyChar.Assign(NS_LITERAL_STRING(" "));
+        }
+      }
 
       nsAutoString modifiersStr;
       keyContent->GetAttr(kNameSpaceID_None, nsGkAtoms::modifiers, modifiersStr);
diff --git a/widget/windows/nsLookAndFeel.cpp b/widget/windows/nsLookAndFeel.cpp
index 844411bfaad6..6d437c8d7f1a 100644
--- a/widget/windows/nsLookAndFeel.cpp
+++ b/widget/windows/nsLookAndFeel.cpp
@@ -46,6 +46,9 @@
 #include "nsStyleConsts.h"
 #include "nsUXThemeData.h"
 #include "nsUXThemeConstants.h"
+#include "WinUtils.h"
+
+using namespace mozilla::widget;
 
 typedef UINT (CALLBACK *SHAppBarMessagePtr)(DWORD, PAPPBARDATA);
 SHAppBarMessagePtr gSHAppBarMessage = NULL;
@@ -199,7 +202,8 @@ nsLookAndFeel::NativeGetColor(ColorID aID, nscolor &aColor)
       idx = COLOR_HIGHLIGHT;
       break;
     case eColorID__moz_menubarhovertext:
-      if (!nsUXThemeData::sIsVistaOrLater || !nsUXThemeData::isAppThemed())
+      if (WinUtils::GetWindowsVersion() < WinUtils::VISTA_VERSION ||
+          !nsUXThemeData::isAppThemed())
       {
         idx = nsUXThemeData::sFlatMenus ?
                 COLOR_HIGHLIGHTTEXT :
@@ -208,7 +212,8 @@ nsLookAndFeel::NativeGetColor(ColorID aID, nscolor &aColor)
       }
       // Fall through
     case eColorID__moz_menuhovertext:
-      if (nsUXThemeData::IsAppThemed() && nsUXThemeData::sIsVistaOrLater)
+      if (WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION &&
+          nsUXThemeData::IsAppThemed())
       {
         res = ::GetColorFromTheme(eUXMenu,
                                   MENU_POPUPITEM, MPI_HOT, TMT_TEXTCOLOR, aColor);
@@ -284,7 +289,8 @@ nsLookAndFeel::NativeGetColor(ColorID aID, nscolor &aColor)
       idx = COLOR_3DFACE;
       break;
     case eColorID__moz_win_mediatext:
-      if (nsUXThemeData::IsAppThemed() && nsUXThemeData::sIsVistaOrLater) {
+      if (WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION &&
+          nsUXThemeData::IsAppThemed()) {
         res = ::GetColorFromTheme(eUXMediaToolbar,
                                   TP_BUTTON, TS_NORMAL, TMT_TEXTCOLOR, aColor);
         if (NS_SUCCEEDED(res))
@@ -294,7 +300,8 @@ nsLookAndFeel::NativeGetColor(ColorID aID, nscolor &aColor)
       idx = COLOR_WINDOWTEXT;
       break;
     case eColorID__moz_win_communicationstext:
-      if (nsUXThemeData::IsAppThemed() && nsUXThemeData::sIsVistaOrLater)
+      if (WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION &&
+          nsUXThemeData::IsAppThemed())
       {
         res = ::GetColorFromTheme(eUXCommunicationsToolbar,
                                   TP_BUTTON, TS_NORMAL, TMT_TEXTCOLOR, aColor);
@@ -519,11 +526,5 @@ PRUnichar
 nsLookAndFeel::GetPasswordCharacterImpl()
 {
 #define UNICODE_BLACK_CIRCLE_CHAR 0x25cf
-  static PRUnichar passwordCharacter = 0;
-  if (!passwordCharacter) {
-    passwordCharacter = '*';
-    if (nsUXThemeData::sIsXPOrLater)
-      passwordCharacter = UNICODE_BLACK_CIRCLE_CHAR;
-  }
-  return passwordCharacter;
+  return UNICODE_BLACK_CIRCLE_CHAR;
 }
diff --git a/widget/windows/nsNativeThemeWin.cpp b/widget/windows/nsNativeThemeWin.cpp
index 2360bd89e2ab..be3002e90d58 100644
--- a/widget/windows/nsNativeThemeWin.cpp
+++ b/widget/windows/nsNativeThemeWin.cpp
@@ -412,7 +412,7 @@ static void OffsetBackgroundRect(RECT& rect, CaptionButton button) {
 HANDLE
 nsNativeThemeWin::GetTheme(PRUint8 aWidgetType)
 { 
-  if (!nsUXThemeData::sIsVistaOrLater) {
+  if (WinUtils::GetWindowsVersion() < WinUtils::VISTA_VERSION) {
     // On XP or earlier, render dropdowns as textfields;
     // doing it the right way works fine with the MS themes,
     // but breaks on a lot of custom themes (presumably because MS
@@ -431,8 +431,9 @@ nsNativeThemeWin::GetTheme(PRUint8 aWidgetType)
     case NS_THEME_TEXTFIELD_MULTILINE:
       return nsUXThemeData::GetTheme(eUXEdit);
     case NS_THEME_TOOLTIP:
-      // BUG #161600: XP/2K3 should force a classic treatment of tooltips
-      return nsUXThemeData::sIsVistaOrLater ? nsUXThemeData::GetTheme(eUXTooltip) : NULL;
+      // XP/2K3 should force a classic treatment of tooltips
+      return WinUtils::GetWindowsVersion() < WinUtils::VISTA_VERSION ?
+        NULL : nsUXThemeData::GetTheme(eUXTooltip);
     case NS_THEME_TOOLBOX:
       return nsUXThemeData::GetTheme(eUXRebar);
     case NS_THEME_WIN_MEDIA_TOOLBOX:
@@ -557,7 +558,7 @@ nsresult
 nsNativeThemeWin::GetThemePartAndState(nsIFrame* aFrame, PRUint8 aWidgetType, 
                                        PRInt32& aPart, PRInt32& aState)
 {
-  if (!nsUXThemeData::sIsVistaOrLater) {
+  if (WinUtils::GetWindowsVersion() < WinUtils::VISTA_VERSION) {
     // See GetTheme
     if (aWidgetType == NS_THEME_DROPDOWN)
       aWidgetType = NS_THEME_TEXTFIELD;
@@ -634,7 +635,7 @@ nsNativeThemeWin::GetThemePartAndState(nsIFrame* aFrame, PRUint8 aWidgetType,
     case NS_THEME_TEXTFIELD_MULTILINE: {
       nsEventStates eventState = GetContentState(aFrame, aWidgetType);
 
-      if (nsUXThemeData::sIsVistaOrLater) {
+      if (WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION) {
         /* Note: the NOSCROLL type has a rounded corner in each
          * corner.  The more specific HSCROLL, VSCROLL, HVSCROLL types
          * have side and/or top/bottom edges rendered as straight
@@ -701,9 +702,11 @@ nsNativeThemeWin::GetThemePartAndState(nsIFrame* aFrame, PRUint8 aWidgetType,
         // we have to return aPart = -1.
         aPart = -1;
       } else if (IsVerticalProgress(stateFrame)) {
-        aPart = nsUXThemeData::sIsVistaOrLater ? PP_FILLVERT : PP_CHUNKVERT;
+        aPart = WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION ?
+          PP_FILLVERT : PP_CHUNKVERT;
       } else {
-        aPart = nsUXThemeData::sIsVistaOrLater ? PP_FILL : PP_CHUNK;
+        aPart = WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION ?
+          PP_FILL : PP_CHUNK;
       }
 
       aState = TS_NORMAL;
@@ -776,7 +779,8 @@ nsNativeThemeWin::GetThemePartAndState(nsIFrame* aFrame, PRUint8 aWidgetType,
           aState += TS_ACTIVE;
         else if (eventState.HasState(NS_EVENT_STATE_HOVER))
           aState += TS_HOVER;
-        else if (nsUXThemeData::sIsVistaOrLater && parentState.HasState(NS_EVENT_STATE_HOVER))
+        else if (WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION &&
+                 parentState.HasState(NS_EVENT_STATE_HOVER))
           aState = (aWidgetType - NS_THEME_SCROLLBAR_BUTTON_UP) + SP_BUTTON_IMPLICIT_HOVER_BASE;
         else
           aState += TS_NORMAL;
@@ -864,7 +868,7 @@ nsNativeThemeWin::GetThemePartAndState(nsIFrame* aFrame, PRUint8 aWidgetType,
     case NS_THEME_SCROLLBAR:
     case NS_THEME_SCROLLBAR_SMALL: {
       aState = 0;
-      if (nsUXThemeData::sIsVistaOrLater) {
+      if (WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION) {
         // On vista, they have a part
         aPart = RP_BACKGROUND;
       } else {
@@ -996,7 +1000,8 @@ nsNativeThemeWin::GetThemePartAndState(nsIFrame* aFrame, PRUint8 aWidgetType,
         aFrame = parentFrame;
 
       nsEventStates eventState = GetContentState(aFrame, aWidgetType);
-      aPart = nsUXThemeData::sIsVistaOrLater ? CBP_DROPMARKER_VISTA : CBP_DROPMARKER;
+      aPart = WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION ?
+        CBP_DROPMARKER_VISTA : CBP_DROPMARKER;
 
       // For HTML controls with author styling, we should fall
       // back to the old dropmarker style to avoid clashes with
@@ -1016,7 +1021,7 @@ nsNativeThemeWin::GetThemePartAndState(nsIFrame* aFrame, PRUint8 aWidgetType,
       else
         isOpen = IsOpenButton(aFrame);
 
-      if (nsUXThemeData::sIsVistaOrLater) {
+      if (WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION) {
         if (isHTML || IsMenuListEditable(aFrame)) {
           if (isOpen) {
             /* Hover is propagated, but we need to know whether we're
@@ -1487,7 +1492,8 @@ RENDER_AGAIN:
 
         // On vista, choose our own colors and draw an XP style half focus rect
         // for focused checkboxes and a full rect when active.
-        if (nsUXThemeData::sIsVistaOrLater && aWidgetType == NS_THEME_CHECKBOX) {
+        if (WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION &&
+            aWidgetType == NS_THEME_CHECKBOX) {
           LOGBRUSH lb;
           lb.lbStyle = BS_SOLID;
           lb.lbColor = RGB(255,255,255);
@@ -1596,7 +1602,8 @@ RENDER_AGAIN:
     bool indeterminate = IsIndeterminateProgress(stateFrame, eventStates);
     bool vertical = IsVerticalProgress(stateFrame);
 
-    if (indeterminate || nsUXThemeData::sIsVistaOrLater) {
+    if (indeterminate ||
+        WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION) {
       if (!QueueAnimatedContentForRefresh(aFrame->GetContent(), 30)) {
         NS_WARNING("unable to animate progress widget!");
       }
@@ -1608,7 +1615,7 @@ RENDER_AGAIN:
        * indeterminate progress bars.
        */
       PRInt32 overlaySize;
-      if (nsUXThemeData::sIsVistaOrLater) {
+      if (WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION) {
         if (vertical) {
           overlaySize = indeterminate ? kProgressVerticalIndeterminateOverlaySize
                                       : kProgressVerticalOverlaySize;
@@ -1652,13 +1659,14 @@ RENDER_AGAIN:
 
       PRInt32 overlayPart;
       if (vertical) {
-        if (nsUXThemeData::sIsVistaOrLater) {
+        if (WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION) {
           overlayPart = indeterminate ? PP_MOVEOVERLAY : PP_MOVEOVERLAYVERT;
         } else {
           overlayPart = PP_CHUNKVERT;
         }
       } else {
-        overlayPart = nsUXThemeData::sIsVistaOrLater ? PP_MOVEOVERLAY : PP_CHUNK;
+        overlayPart = WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION ?
+          PP_MOVEOVERLAY : PP_CHUNK;
       }
 
       nsUXThemeData::drawThemeBG(theme, hdc, overlayPart, state, &overlayRect,
@@ -1823,7 +1831,7 @@ nsNativeThemeWin::GetWidgetPadding(nsDeviceContext* aContext,
     return true;
   }
 
-  if (nsUXThemeData::sIsVistaOrLater) {
+  if (WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION) {
     if (aWidgetType == NS_THEME_TEXTFIELD ||
         aWidgetType == NS_THEME_TEXTFIELD_MULTILINE ||
         aWidgetType == NS_THEME_DROPDOWN)
@@ -1916,7 +1924,7 @@ nsNativeThemeWin::GetWidgetOverflow(nsDeviceContext* aContext,
    * a border only shows up if the widget is being hovered.
    */
 #if 0
-  if (nsUXThemeData::sIsVistaOrLater) {
+  if (WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION) {
     /* We explicitly draw dropdown buttons in HTML content 1px bigger
      * up, right, and bottom so that they overlap the dropdown's border
      * like they're supposed to.
@@ -2042,7 +2050,7 @@ nsNativeThemeWin::GetMinimumWidgetSize(nsRenderingContext* aContext, nsIFrame* a
       *aIsOverridable = false;
       // on Vista, GetThemePartAndState returns odd values for
       // scale thumbs, so use a hardcoded size instead.
-      if (nsUXThemeData::sIsVistaOrLater) {
+      if (WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION) {
         if (aWidgetType == NS_THEME_SCALE_THUMB_HORIZONTAL) {
           aResult->width = 12;
           aResult->height = 20;
@@ -2217,7 +2225,7 @@ nsNativeThemeWin::WidgetStateChanged(nsIFrame* aFrame, PRUint8 aWidgetType,
   }
 
   // On Vista, the scrollbar buttons need to change state when the track has/doesn't have hover
-  if (!nsUXThemeData::sIsVistaOrLater &&
+  if (WinUtils::GetWindowsVersion() < WinUtils::VISTA_VERSION &&
       (aWidgetType == NS_THEME_SCROLLBAR_TRACK_VERTICAL || 
       aWidgetType == NS_THEME_SCROLLBAR_TRACK_HORIZONTAL)) {
     *aShouldRepaint = false;
@@ -2226,7 +2234,7 @@ nsNativeThemeWin::WidgetStateChanged(nsIFrame* aFrame, PRUint8 aWidgetType,
 
   // We need to repaint the dropdown arrow in vista HTML combobox controls when
   // the control is closed to get rid of the hover effect.
-  if (nsUXThemeData::sIsVistaOrLater &&
+  if (WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION &&
       (aWidgetType == NS_THEME_DROPDOWN || aWidgetType == NS_THEME_DROPDOWN_BUTTON) &&
       IsHTMLContent(aFrame))
   {
diff --git a/widget/windows/nsUXThemeData.cpp b/widget/windows/nsUXThemeData.cpp
index f526a507a133..b4ea9d7761a7 100644
--- a/widget/windows/nsUXThemeData.cpp
+++ b/widget/windows/nsUXThemeData.cpp
@@ -69,10 +69,6 @@ nsUXThemeData::sDwmDLL = NULL;
 
 BOOL
 nsUXThemeData::sFlatMenus = FALSE;
-bool
-nsUXThemeData::sIsXPOrLater = false;
-bool
-nsUXThemeData::sIsVistaOrLater = false;
 
 bool nsUXThemeData::sTitlebarInfoPopulatedAero = false;
 bool nsUXThemeData::sTitlebarInfoPopulatedThemed = false;
@@ -121,10 +117,6 @@ nsUXThemeData::Initialize()
   ::ZeroMemory(sThemes, sizeof(sThemes));
   NS_ASSERTION(!sThemeDLL, "nsUXThemeData being initialized twice!");
 
-  WinUtils::WinVersion version = WinUtils::GetWindowsVersion();
-  sIsXPOrLater = version >= WinUtils::WINXP_VERSION;
-  sIsVistaOrLater = version >= WinUtils::VISTA_VERSION;
-
   if (GetThemeDLL()) {
     openTheme = (OpenThemeDataPtr)GetProcAddress(sThemeDLL, "OpenThemeData");
     closeTheme = (CloseThemeDataPtr)GetProcAddress(sThemeDLL, "CloseThemeData");
@@ -166,17 +158,9 @@ nsUXThemeData::Invalidate() {
       sThemes[i] = NULL;
     }
   }
-  if (sIsXPOrLater) {
-    BOOL useFlat = false;
-    sFlatMenus = ::SystemParametersInfo(SPI_GETFLATMENU, 0, &useFlat, 0) ?
-                     useFlat : false;
-  } else {
-    // Contrary to Microsoft's documentation, SPI_GETFLATMENU will not fail
-    // on Windows 2000, and it is also possible (though unlikely) for WIN2K
-    // to be misconfigured in such a way that it would return true, so we
-    // shall give WIN2K special treatment
-    sFlatMenus = false;
-  }
+  BOOL useFlat = false;
+  sFlatMenus = ::SystemParametersInfo(SPI_GETFLATMENU, 0, &useFlat, 0) ?
+                   useFlat : false;
 }
 
 HANDLE
@@ -193,7 +177,7 @@ nsUXThemeData::GetTheme(nsUXThemeClass cls) {
 
 HMODULE
 nsUXThemeData::GetThemeDLL() {
-  if (!sThemeDLL && sIsXPOrLater)
+  if (!sThemeDLL)
     sThemeDLL = ::LoadLibraryW(kThemeLibraryName);
   return sThemeDLL;
 }
@@ -201,7 +185,7 @@ nsUXThemeData::GetThemeDLL() {
 #if MOZ_WINSDK_TARGETVER >= MOZ_NTDDI_LONGHORN
 HMODULE
 nsUXThemeData::GetDwmDLL() {
-  if (!sDwmDLL && sIsVistaOrLater)
+  if (!sDwmDLL && WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION)
     sDwmDLL = ::LoadLibraryW(kDwmLibraryName);
   return sDwmDLL;
 }
diff --git a/widget/windows/nsUXThemeData.h b/widget/windows/nsUXThemeData.h
index 9e9369852e1b..1e6026dd8126 100644
--- a/widget/windows/nsUXThemeData.h
+++ b/widget/windows/nsUXThemeData.h
@@ -128,8 +128,6 @@ public:
    static const PRUnichar kDwmLibraryName[];
 #endif
   static BOOL sFlatMenus;
-  static bool sIsXPOrLater;
-  static bool sIsVistaOrLater;
   static bool sTitlebarInfoPopulatedAero;
   static bool sTitlebarInfoPopulatedThemed;
   static SIZE sCommandButtons[4];
diff --git a/widget/windows/nsWindow.cpp b/widget/windows/nsWindow.cpp
index 9c677d7dbd16..98fcd712d883 100644
--- a/widget/windows/nsWindow.cpp
+++ b/widget/windows/nsWindow.cpp
@@ -1273,7 +1273,8 @@ NS_METHOD nsWindow::IsVisible(bool & bState)
 // transparency. These routines are called on size and move operations.
 void nsWindow::ClearThemeRegion()
 {
-  if (nsUXThemeData::sIsVistaOrLater && !HasGlass() &&
+  if (WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION &&
+      !HasGlass() &&
       (mWindowType == eWindowType_popup && !IsPopupWithTitleBar() &&
        (mPopupType == ePopupTypeTooltip || mPopupType == ePopupTypePanel))) {
     SetWindowRgn(mWnd, NULL, false);
@@ -1287,7 +1288,8 @@ void nsWindow::SetThemeRegion()
   // so default constants are used for part and state. At some point we might need part and
   // state values from nsNativeThemeWin's GetThemePartAndState, but currently windows that
   // change shape based on state haven't come up.
-  if (nsUXThemeData::sIsVistaOrLater && !HasGlass() &&
+  if (WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION &&
+      !HasGlass() &&
       (mWindowType == eWindowType_popup && !IsPopupWithTitleBar() &&
        (mPopupType == ePopupTypeTooltip || mPopupType == ePopupTypePanel))) {
     HRGN hRgn = nsnull;
@@ -6357,7 +6359,7 @@ nsWindow::InitMouseWheelScrollData()
 
   if (!::SystemParametersInfo(SPI_GETWHEELSCROLLCHARS, 0,
                               &sMouseWheelScrollChars, 0)) {
-    NS_ASSERTION(!nsUXThemeData::sIsVistaOrLater,
+    NS_ASSERTION(WinUtils::GetWindowsVersion() < WinUtils::VISTA_VERSION,
                  "Failed to get SPI_GETWHEELSCROLLCHARS");
     sMouseWheelScrollChars = 1;
   } else if (sMouseWheelScrollChars > WHEEL_DELTA) {
diff --git a/xpcom/build/Makefile.in b/xpcom/build/Makefile.in
index d7f00ba2ad87..dc3ee3d10cae 100644
--- a/xpcom/build/Makefile.in
+++ b/xpcom/build/Makefile.in
@@ -79,7 +79,6 @@ SHARED_LIBRARY_LIBS = \
 		../io/$(LIB_PREFIX)xpcomio_s.$(LIB_SUFFIX) \
 		../components/$(LIB_PREFIX)xpcomcomponents_s.$(LIB_SUFFIX) \
 		../threads/$(LIB_PREFIX)xpcomthreads_s.$(LIB_SUFFIX) \
-		../proxy/src/$(LIB_PREFIX)xpcomproxy_s.$(LIB_SUFFIX) \
 		../base/$(LIB_PREFIX)xpcombase_s.$(LIB_SUFFIX) \
 		../reflect/xptcall/src/$(LIB_PREFIX)xptcall.$(LIB_SUFFIX) \
 		../reflect/xptcall/src/$(LIB_PREFIX)xptcmd.$(LIB_SUFFIX) \
diff --git a/xpcom/build/nsXPCOMCIDInternal.h b/xpcom/build/nsXPCOMCIDInternal.h
index bc2185fdfead..be5d17ff3ba0 100644
--- a/xpcom/build/nsXPCOMCIDInternal.h
+++ b/xpcom/build/nsXPCOMCIDInternal.h
@@ -71,12 +71,6 @@
  */
 #define NS_THREADPOOL_CONTRACTID "@mozilla.org/thread-pool;1"
 
-/**
- * The global proxy object manager.  This component is a singleton.
- * @implement nsIProxyObjectManager
- */
-#define NS_XPCOMPROXY_CONTRACTID "@mozilla.org/xpcomproxy;1"
-
 /**
  * The contract id for the nsIXULAppInfo service.
  */
diff --git a/xpcom/glue/nsCycleCollectionParticipant.h b/xpcom/glue/nsCycleCollectionParticipant.h
index 7bcefbebd335..50a55f8da968 100644
--- a/xpcom/glue/nsCycleCollectionParticipant.h
+++ b/xpcom/glue/nsCycleCollectionParticipant.h
@@ -299,6 +299,10 @@ public:
 #define NS_CYCLE_COLLECTION_UPCAST(obj, clazz)                                 \
   NS_CYCLE_COLLECTION_CLASSNAME(clazz)::Upcast(obj)
 
+///////////////////////////////////////////////////////////////////////////////
+// Helpers for implementing CanSkip methods
+///////////////////////////////////////////////////////////////////////////////
+
 #define NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(_class)                        \
   NS_IMETHODIMP_(bool)                                                         \
   NS_CYCLE_COLLECTION_CLASSNAME(_class)::CanSkipReal(void *p,                  \
@@ -310,6 +314,7 @@ public:
     _class *tmp = Downcast(s);
 
 #define NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END                                  \
+    (void)tmp;                                                                 \
     return false;                                                              \
   }
 
@@ -323,6 +328,7 @@ public:
     _class *tmp = Downcast(s);
 
 #define NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END                            \
+    (void)tmp;                                                                 \
     return false;                                                              \
   }
 
@@ -336,6 +342,7 @@ public:
     _class *tmp = Downcast(s);
 
 #define NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END                             \
+    (void)tmp;                                                                 \
     return false;                                                              \
   }
 
@@ -614,6 +621,24 @@ NS_CYCLE_COLLECTION_PARTICIPANT_INSTANCE
 #define NS_DECL_CYCLE_COLLECTION_CLASS(_class)                                 \
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(_class, _class)
 
+// Cycle collector helper for ambiguous classes that can sometimes be skipped.
+#define NS_DECL_CYCLE_COLLECTION_SKIPPABLE_CLASS_AMBIGUOUS(_class, _base)        \
+class NS_CYCLE_COLLECTION_INNERCLASS                                             \
+ : public nsXPCOMCycleCollectionParticipant                                      \
+{                                                                                \
+public:                                                                          \
+  NS_CYCLE_COLLECTION_INNERCLASS () : nsXPCOMCycleCollectionParticipant(true) {} \
+  NS_DECL_CYCLE_COLLECTION_CLASS_BODY(_class, _base)                             \
+protected:                                                                       \
+  NS_IMETHOD_(bool) CanSkipReal(void *p, bool aRemovingAllowed);                 \
+  NS_IMETHOD_(bool) CanSkipInCCReal(void *p);                                    \
+  NS_IMETHOD_(bool) CanSkipThisReal(void *p);                                    \
+};                                                                               \
+NS_CYCLE_COLLECTION_PARTICIPANT_INSTANCE
+
+#define NS_DECL_CYCLE_COLLECTION_SKIPPABLE_CLASS(_class)                       \
+        NS_DECL_CYCLE_COLLECTION_SKIPPABLE_CLASS_AMBIGUOUS(_class, _class)
+
 // Cycle collector helper for classes that don't want to unlink anything.
 // Note: if this is used a lot it might make sense to have a base class that
 //       doesn't do anything in Root/Unlink/Unroot.
diff --git a/xpcom/typelib/xpt/tools/xpt.py b/xpcom/typelib/xpt/tools/xpt.py
index b2034ed2303a..e00a58926587 100644
--- a/xpcom/typelib/xpt/tools/xpt.py
+++ b/xpcom/typelib/xpt/tools/xpt.py
@@ -172,7 +172,7 @@ class Type(object):
         if self.reference:
             flags |= 0x20
         return flags
-    
+
     @staticmethod
     def read(typelib, map, data_pool, offset):
         """
@@ -439,7 +439,7 @@ class StringWithSizeType(Type):
         (size_is_arg_num, length_is_arg_num) = StringWithSizeType._descriptor.unpack(map[start:start + StringWithSizeType._descriptor.size])
         offset += StringWithSizeType._descriptor.size
         return StringWithSizeType(size_is_arg_num, length_is_arg_num, **flags), offset
-    
+
     def write(self, typelib, file):
         """
         Write a StringWithSizeTypeDescriptor to |file|, which is assumed
@@ -449,7 +449,7 @@ class StringWithSizeType(Type):
         Type.write(self, typelib, file)
         file.write(StringWithSizeType._descriptor.pack(self.size_is_arg_num,
                                                        self.length_is_arg_num))
-        
+
     def __str__(self):
         return "string_s"
 
@@ -459,7 +459,7 @@ class WideStringWithSizeType(Type):
     are passed as separate arguments to a method.
     (WideStringWithSizeTypeDescriptor from the typelib specification.)
 
-    """    
+    """
     _descriptor = struct.Struct(">BB")
 
     def __init__(self, size_is_arg_num, length_is_arg_num,
@@ -614,7 +614,7 @@ class Param(object):
         if self.optional:
             s += "optional "
         return s
-            
+
     def __str__(self):
         return self.prefix() + str(self.type)
 
@@ -626,7 +626,7 @@ class Method(object):
     
     """
     _descriptorstart = struct.Struct(">BIB")
-    
+
     def __init__(self, name, result,
                  params=[], getter=False, setter=False, notxpcom=False,
                  constructor=False, hidden=False, optargc=False,
@@ -777,7 +777,7 @@ class Constant(object):
 
     def __init__(self, name, type, value):
         self.name = name
-        self._name_offset = 0        
+        self._name_offset = 0
         self.type = type
         self.value = value
 
@@ -839,11 +839,11 @@ class Interface(object):
     (InterfaceDescriptor from the typelib specification.)
     
     """
-    _direntry = struct.Struct(">16sIII")    
+    _direntry = struct.Struct(">16sIII")
     _descriptorstart = struct.Struct(">HH")
 
     UNRESOLVED_IID = "00000000-0000-0000-0000-000000000000"
-    
+
     def __init__(self, name, iid=UNRESOLVED_IID, namespace="",
                  resolved=False, parent=None, methods=[], constants=[],
                  scriptable=False, function=False, builtinclass=False):
@@ -972,7 +972,7 @@ class Interface(object):
         if self.builtinclass:
             flags |= 0x20
         file.write(struct.pack(">B", flags))
-        
+
     def write_names(self, file, data_pool_offset):
         """
         Write this interface's name and namespace to |file|,
@@ -1016,6 +1016,7 @@ class Typelib(object):
         self.version = version
         self.interfaces = list(interfaces)
         self.annotations = list(annotations)
+        self.filename = None
 
     @staticmethod
     def iid_to_string(iid):
@@ -1025,6 +1026,7 @@ class Typelib(object):
         """
         def hexify(s):
             return ''.join(["%02x" % ord(x) for x in s])
+
         return "%s-%s-%s-%s-%s" % (hexify(iid[:4]), hexify(iid[4:6]),
                                    hexify(iid[6:8]), hexify(iid[8:10]),
                                    hexify(iid[10:]))
@@ -1037,7 +1039,7 @@ class Typelib(object):
         """
         s = iid_str.replace('-','')
         return ''.join([chr(int(s[i:i+2], 16)) for i in range(0, len(s), 2)])
-    
+
     @staticmethod
     def read_string(map, data_pool, offset):
         if offset == 0:
@@ -1046,7 +1048,7 @@ class Typelib(object):
         if sz == -1:
             return ""
         return map[data_pool + offset - 1:sz]
-    
+
     @staticmethod
     def read(filename):
         """
@@ -1061,6 +1063,7 @@ class Typelib(object):
             if data[0] != XPT_MAGIC:
                 raise FileFormatError, "Bad magic: %s" % data[0]
             xpt = Typelib((data[1], data[2]))
+            xpt.filename = filename
             num_interfaces = data[3]
             file_length = data[4]
             if file_length != st.st_size:
@@ -1096,7 +1099,7 @@ class Typelib(object):
             for iface in xpt.interfaces:
                 iface.read_descriptor(xpt, map, data_pool_offset)
         return xpt
-    
+
     def __repr__(self):
         return "<Typelib with %d interfaces>" % len(self.interfaces)
 
@@ -1208,20 +1211,25 @@ class Typelib(object):
                             merged = True
                             # Fixup will happen after processing all interfaces.
                         else:
-                            # Same name, different IIDs, raise an exception
+                            # Same name but different IIDs: raise an exception.
+                            # self.* is the (target) Typelib being merged into,
+                            #   not the one which j.iid was from.
                             raise DataError, \
                                   "Typelibs contain definitions of interface %s" \
-                                    " with different IIDs (%s vs %s)!" % \
-                                    (i.name, i.iid, j.iid)
+                                    " with different IIDs (%s (%s) vs %s (%s))!" % \
+                                    (i.name, i.iid, other.filename, j.iid, self.filename)
                 elif i.iid == j.iid and i.iid != Interface.UNRESOLVED_IID:
-                    # Same IID, different names, raise an exception
+                    # Same IID but different names: raise an exception.
+                    # self.* is the (target) Typelib being merged into,
+                    #   not the one which j.name was from.
                     raise DataError, \
                           "Typelibs contain definitions of interface %s" \
-                            " with different names (%s vs %s)!" % \
-                            (i.iid, i.name, j.name)
+                            " with different names (%s (%s) vs %s (%s))!" % \
+                            (i.iid, i.name, other.filename, j.name, self.filename)
             if not merged:
                 # No partially matching interfaces, so just take this interface
                 self.interfaces.append(i)
+
         # Now fixup any merged interfaces
         def checkType(t, replaced_from, replaced_to):
             if isinstance(t, InterfaceType) and t.iface == replaced_from:
@@ -1230,7 +1238,7 @@ class Typelib(object):
                  isinstance(t.element_type, InterfaceType) and \
                  t.element_type.iface == replaced_from:
                 t.element_type.iface = replaced_to
-        
+
         for replaced_from, replaced_to in merged_interfaces:
             for i in self.interfaces:
                 # Replace parent references