Merge inbound to m-c.
authorRyan VanderMeulen <ryanvm@gmail.com>
Mon, 09 Sep 2013 15:55:42 -0400
changeset 146201 f320b8c034bd81f0a1ff29d0dd1fe6247ce42a32
parent 146163 9edc229b7d09c689d870ca6be7a8a3dd655d6088 (current diff)
parent 146200 77841a0ee5309a54b64806fc5ed96bbada6dd66b (diff)
child 146216 a468b2e34b043362715ea032ca204d35bd99fc9f
push id25244
push userryanvm@gmail.com
push dateMon, 09 Sep 2013 20:03:14 +0000
treeherdermozilla-central@f320b8c034bd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone26.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to m-c.
content/canvas/src/ImageEncoder.cpp
content/canvas/src/ImageEncoder.h
content/canvas/src/WebGLMemoryMultiReporterWrapper.h
js/src/vm/GlobalObject-inl.h
--- a/accessible/src/base/RoleMap.h
+++ b/accessible/src/base/RoleMap.h
@@ -614,17 +614,17 @@ ROLE(FONT_CHOOSER,
      NSAccessibilityUnknownRole,
      USE_ROLE_STRING,
      IA2_ROLE_FONT_CHOOSER,
      eNoNameRule)
 
 ROLE(CHROME_WINDOW,
      "chrome window",
      ATK_ROLE_FRAME,
-     NSAccessibilityUnknownRole,  //Unused on OS X
+     NSAccessibilityGroupRole,  //Contains the main Firefox UI
      ROLE_SYSTEM_APPLICATION,
      IA2_ROLE_FRAME,
      eNoNameRule)
 
 ROLE(GLASS_PANE,
      "glass pane",
      ATK_ROLE_GLASS_PANE,
      NSAccessibilityGroupRole,
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1095,16 +1095,17 @@ pref("devtools.responsiveUI.no-reload-no
 // Enable the Debugger
 pref("devtools.debugger.enabled", true);
 pref("devtools.debugger.chrome-enabled", true);
 pref("devtools.debugger.chrome-debugging-host", "localhost");
 pref("devtools.debugger.chrome-debugging-port", 6080);
 pref("devtools.debugger.remote-host", "localhost");
 pref("devtools.debugger.remote-timeout", 20000);
 pref("devtools.debugger.pause-on-exceptions", false);
+pref("devtools.debugger.ignore-caught-exceptions", true);
 pref("devtools.debugger.source-maps-enabled", true);
 
 // The default Debugger UI settings
 pref("devtools.debugger.ui.panes-sources-width", 200);
 pref("devtools.debugger.ui.panes-instruments-width", 300);
 pref("devtools.debugger.ui.panes-visible-on-startup", false);
 pref("devtools.debugger.ui.variables-sorting-enabled", true);
 pref("devtools.debugger.ui.variables-only-enum-visible", false);
--- a/browser/base/content/popup-notifications.inc
+++ b/browser/base/content/popup-notifications.inc
@@ -24,16 +24,22 @@
                accesskey="&getUserMedia.selectMicrophone.accesskey;"
                control="webRTC-selectMicrophone-menulist"/>
         <menulist id="webRTC-selectMicrophone-menulist">
           <menupopup id="webRTC-selectMicrophone-menupopup"/>
         </menulist>
       </popupnotificationcontent>
     </popupnotification>
 
+    <popupnotification id="webapps-install-progress-notification" hidden="true">
+      <popupnotificationcontent id="webapps-install-progress-content" orient="vertical" align="start">
+        <separator class="thin"/>
+      </popupnotificationcontent>
+    </popupnotification>
+
     <popupnotification id="geolocation-notification" hidden="true">
       <popupnotificationcontent orient="vertical" align="start">
         <separator class="thin"/>
         <label id="geolocation-learnmore-link" class="text-link"/>
       </popupnotificationcontent>
     </popupnotification>
 
     <popupnotification id="servicesInstall-notification" hidden="true">
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -3123,22 +3123,25 @@
       <handler event="oop-browser-crashed">
         <![CDATA[
           if (!event.isTrusted)
             return;
 
           let browser = event.originalTarget;
           let title = browser.contentTitle;
           let uri = browser.currentURI;
+          let icon = browser.mIconURL;
 
           this._updateBrowserRemoteness(browser, false);
 
           browser.setAttribute("crashedPageTitle", title);
           browser.docShell.displayLoadError(Cr.NS_ERROR_CONTENT_CRASHED, uri, null);
           browser.removeAttribute("crashedPageTitle");
+          let tab = this._getTabForBrowser(browser);
+          this.setIcon(tab, icon);
         ]]>
       </handler>
     </handlers>
   </binding>
 
   <binding id="tabbrowser-tabbox"
            extends="chrome://global/content/bindings/tabbox.xml#tabbox">
     <implementation>
--- a/browser/devtools/debugger/debugger-controller.js
+++ b/browser/devtools/debugger/debugger-controller.js
@@ -379,17 +379,18 @@ ThreadState.prototype = {
 
   /**
    * Connect to the current thread client.
    */
   connect: function() {
     dumpn("ThreadState is connecting...");
     this.activeThread.addListener("paused", this._update);
     this.activeThread.addListener("resumed", this._update);
-    this.activeThread.pauseOnExceptions(Prefs.pauseOnExceptions);
+    this.activeThread.pauseOnExceptions(Prefs.pauseOnExceptions,
+                                        Prefs.ignoreCaughtExceptions);
     this._handleTabNavigation();
   },
 
   /**
    * Disconnect from the client.
    */
   disconnect: function() {
     if (!this.activeThread) {
@@ -1552,16 +1553,17 @@ let Prefs = new ViewHelpers.Prefs("devto
   chromeDebuggingPort: ["Int", "chrome-debugging-port"],
   sourcesWidth: ["Int", "ui.panes-sources-width"],
   instrumentsWidth: ["Int", "ui.panes-instruments-width"],
   panesVisibleOnStartup: ["Bool", "ui.panes-visible-on-startup"],
   variablesSortingEnabled: ["Bool", "ui.variables-sorting-enabled"],
   variablesOnlyEnumVisible: ["Bool", "ui.variables-only-enum-visible"],
   variablesSearchboxVisible: ["Bool", "ui.variables-searchbox-visible"],
   pauseOnExceptions: ["Bool", "pause-on-exceptions"],
+  ignoreCaughtExceptions: ["Bool", "ignore-caught-exceptions"],
   sourceMapsEnabled: ["Bool", "source-maps-enabled"]
 });
 
 /**
  * Returns true if this is a chrome debugger instance.
  * @return boolean
  */
 XPCOMUtils.defineLazyGetter(window, "_isChromeDebugger", function() {
--- a/browser/devtools/debugger/debugger-toolbar.js
+++ b/browser/devtools/debugger/debugger-toolbar.js
@@ -185,37 +185,40 @@ ToolbarView.prototype = {
 
 /**
  * Functions handling the options UI.
  */
 function OptionsView() {
   dumpn("OptionsView was instantiated");
 
   this._togglePauseOnExceptions = this._togglePauseOnExceptions.bind(this);
+  this._toggleIgnoreCaughtExceptions = this._toggleIgnoreCaughtExceptions.bind(this);
   this._toggleShowPanesOnStartup = this._toggleShowPanesOnStartup.bind(this);
   this._toggleShowVariablesOnlyEnum = this._toggleShowVariablesOnlyEnum.bind(this);
   this._toggleShowVariablesFilterBox = this._toggleShowVariablesFilterBox.bind(this);
   this._toggleShowOriginalSource = this._toggleShowOriginalSource.bind(this);
 }
 
 OptionsView.prototype = {
   /**
    * Initialization function, called when the debugger is started.
    */
   initialize: function() {
     dumpn("Initializing the OptionsView");
 
     this._button = document.getElementById("debugger-options");
     this._pauseOnExceptionsItem = document.getElementById("pause-on-exceptions");
+    this._ignoreCaughtExceptionsItem = document.getElementById("ignore-caught-exceptions");
     this._showPanesOnStartupItem = document.getElementById("show-panes-on-startup");
     this._showVariablesOnlyEnumItem = document.getElementById("show-vars-only-enum");
     this._showVariablesFilterBoxItem = document.getElementById("show-vars-filter-box");
     this._showOriginalSourceItem = document.getElementById("show-original-source");
 
     this._pauseOnExceptionsItem.setAttribute("checked", Prefs.pauseOnExceptions);
+    this._ignoreCaughtExceptionsItem.setAttribute("checked", Prefs.ignoreCaughtExceptions);
     this._showPanesOnStartupItem.setAttribute("checked", Prefs.panesVisibleOnStartup);
     this._showVariablesOnlyEnumItem.setAttribute("checked", Prefs.variablesOnlyEnumVisible);
     this._showVariablesFilterBoxItem.setAttribute("checked", Prefs.variablesSearchboxVisible);
     this._showOriginalSourceItem.setAttribute("checked", Prefs.sourceMapsEnabled);
   },
 
   /**
    * Destruction function, called when the debugger is closed.
@@ -245,20 +248,31 @@ OptionsView.prototype = {
   _onPopupHidden: function() {
     window.dispatchEvent(document, "Debugger:OptionsPopupHidden");
   },
 
   /**
    * Listener handling the 'pause on exceptions' menuitem command.
    */
   _togglePauseOnExceptions: function() {
-    let pref = Prefs.pauseOnExceptions =
+    Prefs.pauseOnExceptions =
       this._pauseOnExceptionsItem.getAttribute("checked") == "true";
 
-    DebuggerController.activeThread.pauseOnExceptions(pref);
+    DebuggerController.activeThread.pauseOnExceptions(
+      Prefs.pauseOnExceptions,
+      Prefs.ignoreCaughtExceptions);
+  },
+
+  _toggleIgnoreCaughtExceptions: function() {
+    Prefs.ignoreCaughtExceptions =
+      this._ignoreCaughtExceptionsItem.getAttribute("checked") == "true";
+
+    DebuggerController.activeThread.pauseOnExceptions(
+      Prefs.pauseOnExceptions,
+      Prefs.ignoreCaughtExceptions);
   },
 
   /**
    * Listener handling the 'show panes on startup' menuitem command.
    */
   _toggleShowPanesOnStartup: function() {
     Prefs.panesVisibleOnStartup =
       this._showPanesOnStartupItem.getAttribute("checked") == "true";
--- a/browser/devtools/debugger/debugger.xul
+++ b/browser/devtools/debugger/debugger.xul
@@ -65,16 +65,18 @@
     <command id="addConditionalBreakpointCommand"
              oncommand="DebuggerView.Sources._onCmdAddConditionalBreakpoint()"/>
     <command id="addWatchExpressionCommand"
              oncommand="DebuggerView.WatchExpressions._onCmdAddExpression()"/>
     <command id="removeAllWatchExpressionsCommand"
              oncommand="DebuggerView.WatchExpressions._onCmdRemoveAllExpressions()"/>
     <command id="togglePauseOnExceptions"
              oncommand="DebuggerView.Options._togglePauseOnExceptions()"/>
+    <command id="toggleIgnoreCaughtExceptions"
+             oncommand="DebuggerView.Options._toggleIgnoreCaughtExceptions()"/>
     <command id="toggleShowPanesOnStartup"
              oncommand="DebuggerView.Options._toggleShowPanesOnStartup()"/>
     <command id="toggleShowOnlyEnum"
              oncommand="DebuggerView.Options._toggleShowVariablesOnlyEnum()"/>
     <command id="toggleShowVariablesFilterBox"
              oncommand="DebuggerView.Options._toggleShowVariablesFilterBox()"/>
     <command id="toggleShowOriginalSource"
              oncommand="DebuggerView.Options._toggleShowOriginalSource()"/>
@@ -155,16 +157,21 @@
                onpopupshowing="DebuggerView.Options._onPopupShowing()"
                onpopuphiding="DebuggerView.Options._onPopupHiding()"
                onpopuphidden="DebuggerView.Options._onPopupHidden()">
       <menuitem id="pause-on-exceptions"
                 type="checkbox"
                 label="&debuggerUI.pauseExceptions;"
                 accesskey="&debuggerUI.pauseExceptions.key;"
                 command="togglePauseOnExceptions"/>
+      <menuitem id="ignore-caught-exceptions"
+                type="checkbox"
+                label="&debuggerUI.ignoreCaughtExceptions;"
+                accesskey="&debuggerUI.ignoreCaughtExceptions.key;"
+                command="toggleIgnoreCaughtExceptions"/>
       <menuitem id="show-panes-on-startup"
                 type="checkbox"
                 label="&debuggerUI.showPanesOnInit;"
                 accesskey="&debuggerUI.showPanesOnInit.key;"
                 command="toggleShowPanesOnStartup"/>
       <menuitem id="show-vars-only-enum"
                 type="checkbox"
                 label="&debuggerUI.showOnlyEnum;"
--- a/browser/devtools/debugger/test/browser_dbg_pause-exceptions-reload.js
+++ b/browser/devtools/debugger/test/browser_dbg_pause-exceptions-reload.js
@@ -33,16 +33,20 @@ function test()
 function testWithFrame()
 {
   // Pause on exceptions should be already enabled.
   is(gDebugger.Prefs.pauseOnExceptions, true,
     "The pause-on-exceptions pref should be true from startup.");
   is(gDebugger.DebuggerView.Options._pauseOnExceptionsItem.getAttribute("checked"), "true",
     "Pause on exceptions should be enabled from startup. ")
 
+  // Disable ignore caught exceptions
+  gDebugger.DebuggerView.Options._ignoreCaughtExceptionsItem.setAttribute("checked", "false");
+  gDebugger.DebuggerView.Options._toggleIgnoreCaughtExceptions();
+
   let count = 0;
   gPane.panelWin.gClient.addOneTimeListener("paused", function() {
     gDebugger.addEventListener("Debugger:FetchedVariables", function testA() {
       // We expect 2 Debugger:FetchedVariables events, one from the global object
       // scope and the regular one.
       if (++count < 2) {
         is(count, 1, "A. First Debugger:FetchedVariables event received.");
         return;
@@ -98,16 +102,20 @@ function testWithFrame()
   content.window.location.reload();
 }
 
 function resumeAndFinish() {
   // Disable pause on exceptions.
   gDebugger.DebuggerView.Options._pauseOnExceptionsItem.setAttribute("checked", "false");
   gDebugger.DebuggerView.Options._togglePauseOnExceptions();
 
+  // Enable ignore caught exceptions
+  gDebugger.DebuggerView.Options._ignoreCaughtExceptionsItem.setAttribute("checked", "true");
+  gDebugger.DebuggerView.Options._toggleIgnoreCaughtExceptions();
+
   is(gDebugger.Prefs.pauseOnExceptions, false,
     "The pause-on-exceptions pref should have been set to false.");
 
   gPane.panelWin.gClient.addOneTimeListener("resumed", function() {
     Services.tm.currentThread.dispatch({ run: closeDebuggerAndFinish }, 0);
   });
 
   // Resume to let the exception reach it's catch clause.
--- a/browser/devtools/debugger/test/browser_dbg_pause-exceptions.js
+++ b/browser/devtools/debugger/test/browser_dbg_pause-exceptions.js
@@ -53,16 +53,20 @@ function testWithFrame()
       // Pause on exceptions should be already enabled.
       is(gPrevPref, false,
         "The pause-on-exceptions functionality should be disabled by default.");
       is(gDebugger.Prefs.pauseOnExceptions, true,
         "The pause-on-exceptions pref should be true from startup.");
       is(gDebugger.DebuggerView.Options._pauseOnExceptionsItem.getAttribute("checked"), "true",
         "Pause on exceptions should be enabled from startup. ")
 
+      // Disable ignore caught exceptions
+      gDebugger.DebuggerView.Options._ignoreCaughtExceptionsItem.setAttribute("checked", "false");
+      gDebugger.DebuggerView.Options._toggleIgnoreCaughtExceptions();
+
       count = 0;
       gPane.panelWin.gClient.addOneTimeListener("resumed", function() {
         gDebugger.addEventListener("Debugger:FetchedVariables", function testB() {
           // We expect 2 Debugger:FetchedVariables events, one from the global object
           // scope and the regular one.
           if (++count < 2) {
             is(count, 1, "B. First Debugger:FetchedVariables event received.");
             return;
@@ -89,16 +93,19 @@ function testWithFrame()
 
             is(innerNodes[0].querySelector(".value").getAttribute("value"), "Error",
               "Should have the right property value for the exception.");
 
             // Disable pause on exceptions.
             gDebugger.DebuggerView.Options._pauseOnExceptionsItem.setAttribute("checked", "false");
             gDebugger.DebuggerView.Options._togglePauseOnExceptions();
 
+            gDebugger.DebuggerView.Options._ignoreCaughtExceptionsItem.setAttribute("checked", "true");
+            gDebugger.DebuggerView.Options._toggleIgnoreCaughtExceptions();
+
             is(gDebugger.Prefs.pauseOnExceptions, false,
               "The pause-on-exceptions pref should have been set to false.");
 
             resumeAndFinish();
 
           }}, 0);
         }, false);
       });
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -402,16 +402,17 @@ dataReportingNotification.button.label  
 dataReportingNotification.button.accessKey  = C
 
 # Webapps notification popup
 webapps.install = Install
 webapps.install.accesskey = I
 #LOCALIZATION NOTE (webapps.requestInstall) %1$S is the web app name, %2$S is the site from which the web app is installed
 webapps.requestInstall = Do you want to install "%1$S" from this site (%2$S)?
 webapps.install.success = Application Installed
+webapps.install.inprogress = Installation in progress
 
 # LOCALIZATION NOTE (fullscreen.entered): displayed when we enter HTML5 fullscreen mode, %S is the domain name of the focused website (e.g. mozilla.com).
 fullscreen.entered=%S is now fullscreen.
 # LOCALIZATION NOTE (fullscreen.rememberDecision): displayed when we enter HTML5 fullscreen mode, %S is the domain name of the focused website (e.g. mozilla.com).
 fullscreen.rememberDecision=Remember decision for %S
 
 service.toolbarbutton.label=Services
 service.toolbarbutton.tooltiptext=Services
--- a/browser/locales/en-US/chrome/browser/devtools/debugger.dtd
+++ b/browser/locales/en-US/chrome/browser/devtools/debugger.dtd
@@ -33,16 +33,21 @@
   -  the button that opens up an options context menu for the debugger UI. -->
 <!ENTITY debuggerUI.optsButton.tooltip  "Debugger Options">
 
 <!-- LOCALIZATION NOTE (debuggerUI.pauseExceptions): This is the label for the
   -  checkbox that toggles pausing on exceptions. -->
 <!ENTITY debuggerUI.pauseExceptions     "Pause on exceptions">
 <!ENTITY debuggerUI.pauseExceptions.key "E">
 
+<!-- LOCALIZATION NOTE (debuggerUI.pauseExceptions): This is the label for the
+  -  checkbox that toggles ignoring caught exceptions. -->
+<!ENTITY debuggerUI.ignoreCaughtExceptions     "Ignore caught exceptions">
+<!ENTITY debuggerUI.ignoreCaughtExceptions.key "C">
+
 <!-- LOCALIZATION NOTE (debuggerUI.showPanesOnInit): This is the label for the
   -  checkbox that toggles visibility of panes when opening the debugger. -->
 <!ENTITY debuggerUI.showPanesOnInit     "Show panes on startup">
 <!ENTITY debuggerUI.showPanesOnInit.key "S">
 
 <!-- LOCALIZATION NOTE (debuggerUI.showVarsFilter): This is the label for the
   -  checkbox that toggles visibility of a designated variables filter box. -->
 <!ENTITY debuggerUI.showVarsFilter      "Show variables filter box">
--- a/browser/modules/webappsUI.jsm
+++ b/browser/modules/webappsUI.jsm
@@ -112,23 +112,41 @@ this.webappsUI = {
     }
   },
 
   doInstall: function(aData, aWindow) {
     let browser = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
                          .getInterface(Ci.nsIWebNavigation)
                          .QueryInterface(Ci.nsIDocShell)
                          .chromeEventHandler;
-    let chromeWin = browser.ownerDocument.defaultView;
+    let chromeDoc = browser.ownerDocument;
+    let chromeWin = chromeDoc.defaultView;
+    let popupProgressContent =
+      chromeDoc.getElementById("webapps-install-progress-content");
+
     let bundle = chromeWin.gNavigatorBundle;
 
+    let notification;
+
     let mainAction = {
       label: bundle.getString("webapps.install"),
       accessKey: bundle.getString("webapps.install.accesskey"),
       callback: () => {
+        notification.remove();
+
+        notification = chromeWin.PopupNotifications.
+                        show(browser,
+                             "webapps-install-progress",
+                             bundle.getString("webapps.install.inprogress"),
+                             "webapps-notification-icon");
+
+        let progressMeter = chromeDoc.createElement("progressmeter");
+        progressMeter.setAttribute("mode", "undetermined");
+        popupProgressContent.appendChild(progressMeter);
+
         let manifestURL = aData.app.manifestURL;
         if (aData.app.manifest && aData.app.manifest.appcache_path) {
           this.downloads[manifestURL] = Promise.defer();
         }
 
         let app = WebappsInstaller.init(aData);
 
         if (app) {
@@ -145,16 +163,18 @@ this.webappsUI = {
                   if (this.downloads[manifestURL]) {
                     yield this.downloads[manifestURL].promise;
                   }
                   installationSuccessNotification(aData, app, bundle);
                 } catch (ex) {
                   Cu.reportError("Error installing webapp: " + ex);
                   // TODO: Notify user that the installation has failed
                 } finally {
+                  popupProgressContent.removeChild(progressMeter);
+                  notification.remove();
                   delete this.downloads[manifestURL];
                 }
               }.bind(this));
             });
         } else {
           DOMApplicationRegistry.denyInstall(aData);
         }
       }
@@ -169,18 +189,21 @@ this.webappsUI = {
       host = requestingURI.host;
     } catch(e) {
       host = requestingURI.spec;
     }
 
     let message = bundle.getFormattedString("webapps.requestInstall",
                                             [manifest.name, host], 2);
 
-    chromeWin.PopupNotifications.show(browser, "webapps-install", message,
-                                    "webapps-notification-icon", mainAction);
+    notification = chromeWin.PopupNotifications.show(browser,
+                                                     "webapps-install",
+                                                     message,
+                                                     "webapps-notification-icon",
+                                                     mainAction);
 
   }
 }
 
 function installationSuccessNotification(aData, app, aBundle) {
   let launcher = {
     observe: function(aSubject, aTopic) {
       if (aTopic == "alertclickcallback") {
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
@@ -1179,16 +1179,17 @@ toolbar[iconsize="small"] #webrtc-status
   list-style-image: url(chrome://global/skin/icons/question-64.png);
 }
 
 .popup-notification-icon[popupid="password-save"],
 .popup-notification-icon[popupid="password-change"] {
   list-style-image: url(chrome://mozapps/skin/passwordmgr/key-64.png);
 }
 
+.popup-notification-icon[popupid="webapps-install-progress"],
 .popup-notification-icon[popupid="webapps-install"] {
   list-style-image: url(chrome://global/skin/icons/webapps-64.png);
 }
 
 .popup-notification-icon[popupid="mixed-content-blocked"] {
   list-style-image: url(chrome://browser/skin/mixed-content-blocked-64.png);
 }
 
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -3400,16 +3400,17 @@ toolbarbutton.chevron > .toolbarbutton-m
   list-style-image: url(chrome://global/skin/icons/question-64.png);
 }
 
 .popup-notification-icon[popupid="password-save"],
 .popup-notification-icon[popupid="password-change"] {
   list-style-image: url(chrome://mozapps/skin/passwordmgr/key-64.png);
 }
 
+.popup-notification-icon[popupid="webapps-install-progress"],
 .popup-notification-icon[popupid="webapps-install"] {
   list-style-image: url(chrome://global/skin/icons/webapps-64.png);
 }
 
 .popup-notification-icon[popupid="mixed-content-blocked"] {
   list-style-image: url(chrome://browser/skin/mixed-content-blocked-64.png);
 }
 @media (min-resolution: 2dppx) {
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -2453,16 +2453,17 @@ toolbarbutton.bookmark-item[dragover="tr
   list-style-image: url(chrome://global/skin/icons/question-64.png);
 }
 
 .popup-notification-icon[popupid="password-save"],
 .popup-notification-icon[popupid="password-change"] {
   list-style-image: url(chrome://mozapps/skin/passwordmgr/key-64.png);
 }
 
+.popup-notification-icon[popupid="webapps-install-progress"],
 .popup-notification-icon[popupid="webapps-install"] {
   list-style-image: url(chrome://global/skin/icons/webapps-64.png);
 }
 
 .popup-notification-icon[popupid="mixed-content-blocked"] {
   list-style-image: url(chrome://browser/skin/mixed-content-blocked-64.png);
 }
 
new file mode 100644
--- /dev/null
+++ b/build/annotationProcessors/AnnotationProcessor.java
@@ -0,0 +1,76 @@
+/* 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/. */
+
+package org.mozilla.gecko.annotationProcessors;
+
+import org.mozilla.gecko.annotationProcessors.classloader.IterableJarLoadingURLClassLoader;
+import org.mozilla.gecko.annotationProcessors.utils.GeneratableEntryPointIterator;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Iterator;
+
+public class AnnotationProcessor {
+    public static final String OUTFILE = "GeneratedJNIWrappers.cpp";
+    public static final String HEADERFILE = "GeneratedJNIWrappers.h";
+
+    public static void main(String[] args) {
+        // We expect a list of jars on the commandline. If missing, whinge about it.
+        if (args.length <= 1) {
+            System.err.println("Usage: java AnnotationProcessor jarfiles ...");
+            System.exit(1);
+        }
+
+        System.out.println("Processing annotations...");
+
+        // We want to produce the same output as last time as often as possible. Ordering of
+        // generated statements, therefore, needs to be consistent.
+        Arrays.sort(args);
+
+        // Start the clock!
+        long s = System.currentTimeMillis();
+
+        // Get an iterator over the classes in the jar files given...
+        Iterator<Class<?>> jarClassIterator = IterableJarLoadingURLClassLoader.getIteratorOverJars(args);
+
+        CodeGenerator generatorInstance = new CodeGenerator();
+
+        while (jarClassIterator.hasNext()) {
+            Class<?> aClass = jarClassIterator.next();
+
+            // Get an iterator over the appropriately generated methods of this class
+            Iterator<MethodWithAnnotationInfo> methodIterator = new GeneratableEntryPointIterator(aClass.getDeclaredMethods());
+
+            // Iterate all annotated methods in this class..
+            while (methodIterator.hasNext()) {
+                MethodWithAnnotationInfo aMethodTuple = methodIterator.next();
+                generatorInstance.generateMethod(aMethodTuple, aClass);
+            }
+
+        }
+
+        writeOutputFiles(generatorInstance);
+        long e = System.currentTimeMillis();
+        System.out.println("Annotation processing complete in " + (e - s) + "ms");
+    }
+
+    private static void writeOutputFiles(CodeGenerator aGenerator) {
+        try {
+            FileOutputStream outStream = new FileOutputStream(OUTFILE);
+            outStream.write(aGenerator.getWrapperFileContents());
+        } catch (IOException e) {
+            System.err.println("Unable to write " + OUTFILE + ". Perhaps a permissions issue?");
+            e.printStackTrace(System.err);
+        }
+
+        try {
+            FileOutputStream headerStream = new FileOutputStream(HEADERFILE);
+            headerStream.write(aGenerator.getHeaderFileContents());
+        } catch (IOException e) {
+            System.err.println("Unable to write " + OUTFILE + ". Perhaps a permissions issue?");
+            e.printStackTrace(System.err);
+        }
+    }
+}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/build/annotationProcessors/CodeGenerator.java
@@ -0,0 +1,341 @@
+/* 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/. */
+
+package org.mozilla.gecko.annotationProcessors;
+
+import org.mozilla.gecko.annotationProcessors.utils.Utils;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.HashMap;
+import java.util.HashSet;
+
+public class CodeGenerator {
+    // Buffers holding the strings to ultimately be written to the output files.
+    private final StringBuilder wrapperStartupCode = new StringBuilder();
+    private final StringBuilder wrapperMethodBodies = new StringBuilder();
+    private final StringBuilder headerFields = new StringBuilder();
+    private final StringBuilder headerMethods = new StringBuilder();
+
+    private final HashSet<String> seenClasses = new HashSet<String>();
+
+    private final String GENERATED_COMMENT = "// GENERATED CODE\n" +
+            "// Generated by the Java program at /build/annotationProcessors at compile time from\n" +
+            "// annotations on Java methods. To update, change the annotations on the corresponding Java\n" +
+            "// methods and rerun the build. Manually updating this file will cause your build to fail.\n\n";
+
+    public CodeGenerator() {
+        // Write the file header things. Includes and so forth.
+        // GeneratedJNIWrappers.cpp is generated as the concatenation of wrapperStartupCode with
+        // wrapperMethodBodies. Similarly, GeneratedJNIWrappers.h is the concatenation of headerFields
+        // with headerMethods.
+        wrapperStartupCode.append(GENERATED_COMMENT);
+        wrapperStartupCode.append(
+                "#include \"nsXPCOMStrings.h\"\n" +
+                "#include \"AndroidBridge.h\"\n" +
+                "#include \"AndroidBridgeUtilities.h\"\n" +
+                "\n" +
+                "#ifdef DEBUG\n" +
+                "#define ALOG_BRIDGE(args...) ALOG(args)\n" +
+                "#else\n" +
+                "#define ALOG_BRIDGE(args...) ((void)0)\n" +
+                "#endif\n" +
+                "\n" +
+                "using namespace mozilla;\n" +
+                "void AndroidBridge::InitStubs(JNIEnv *jEnv) {\n" +
+                "    ALOG_BRIDGE(\"%s\", __PRETTY_FUNCTION__);\n" +
+                "    initInit();\n");
+        // Now we write the various GetStaticMethodID calls here...
+
+        headerFields.append("protected:\n\n");
+        headerMethods.append(GENERATED_COMMENT);
+        headerMethods.append("public:\n\n");
+    }
+
+    /**
+     * Append the appropriate generated code to the buffers for the method provided.
+     *
+     * @param aMethodTuple The Java method, plus the name for the generated method.
+     * @param aClass       The class to which the Java method belongs.
+     */
+    public void generateMethod(MethodWithAnnotationInfo aMethodTuple, Class<?> aClass) {
+        // Unpack the tuple and extract some useful fields from the Method..
+        Method aMethod = aMethodTuple.method;
+        String CMethodName = aMethodTuple.wrapperName;
+
+        String javaMethodName = aMethod.getName();
+
+        ensureClassHeaderAndStartup(aClass);
+
+        writeHeaderField(CMethodName);
+        writeStartupCode(CMethodName, javaMethodName, aMethod, aClass);
+
+        // Get the C++ method signature for this method.
+        String implementationSignature = Utils.getCImplementationMethodSignature(aMethod, CMethodName);
+        String headerSignature = Utils.getCHeaderMethodSignature(aMethod, CMethodName, aMethodTuple.isStatic);
+
+        // Add the header signature to the header file.
+        headerMethods.append(headerSignature);
+        headerMethods.append(";\n");
+
+        // Use the implementation signature to generate the method body...
+        writeMethodBody(implementationSignature, CMethodName, aMethod, aClass, aMethodTuple.isStatic, aMethodTuple.isMultithreaded);
+    }
+
+    /**
+     * Writes the appropriate header and startup code to ensure the existence of a reference to the
+     * class specified. If this is already done, does nothing.
+     *
+     * @param aClass The target class.
+     */
+    private void ensureClassHeaderAndStartup(Class<?> aClass) {
+        String className = aClass.getCanonicalName();
+        if (seenClasses.contains(className)) {
+            return;
+        }
+
+        // Add a field to hold the reference...
+        headerFields.append("\njclass ");
+        headerFields.append(Utils.getClassReferenceName(aClass));
+        headerFields.append(";\n");
+
+        // Add startup code to populate it..
+        wrapperStartupCode.append('\n');
+        wrapperStartupCode.append(Utils.getStartupLineForClass(aClass));
+
+        seenClasses.add(className);
+    }
+
+    /**
+     * Generates the method body of the C++ wrapper function for the Java method indicated.
+     *
+     * @param methodSignature The previously-generated C++ method signature for the method to be
+     *                        generated.
+     * @param aCMethodName    The C++ method name for the method to be generated.
+     * @param aMethod         The Java method to be wrapped by the C++ method being generated.
+     * @param aClass          The Java class to which the method belongs.
+     */
+    private void writeMethodBody(String methodSignature, String aCMethodName, Method aMethod, Class<?> aClass, boolean aIsStaticBridgeMethod, boolean aIsMultithreaded) {
+        Class<?>[] argumentTypes = aMethod.getParameterTypes();
+        Class<?> returnType = aMethod.getReturnType();
+
+        // The start-of-function boilerplate. Does the bridge exist? Does the env exist? etc.
+        wrapperMethodBodies.append('\n');
+        wrapperMethodBodies.append(methodSignature);
+
+        wrapperMethodBodies.append(" {\n" +
+                                   "    ALOG_BRIDGE(\"%s\", __PRETTY_FUNCTION__);\n");
+
+        // Static stubs check the bridge instance has been created before trying to run.
+        if (aIsStaticBridgeMethod) {
+            wrapperMethodBodies.append("    if (!sBridge) {\n" +
+                                       "        ALOG_BRIDGE(\"Aborted: No sBridge - %s\", __PRETTY_FUNCTION__);\n" +
+                                       "        return").append(Utils.getFailureReturnForType(returnType)).append(";\n" +
+                                       "    }\n\n");
+        }
+        wrapperMethodBodies.append("    JNIEnv *env = ");
+        if (!aIsMultithreaded) {
+            wrapperMethodBodies.append("GetJNIEnv();\n");
+        } else {
+            wrapperMethodBodies.append("GetJNIForThread();\n");
+        }
+        wrapperMethodBodies.append("    if (!env) {\n" +
+                                   "        ALOG_BRIDGE(\"Aborted: No env - %s\", __PRETTY_FUNCTION__);\n" +
+                                   "        return").append(Utils.getFailureReturnForType(returnType)).append(";\n" +
+                                   "    }\n\n");
+
+        boolean isObjectReturningMethod = !returnType.getCanonicalName().equals("void") && Utils.doesReturnObjectType(aMethod);
+
+        // Determine the number of local refs required for our local frame..
+        // AutoLocalJNIFrame is not applicable here due to it's inability to handle return values.
+        int localReferencesNeeded = Utils.enumerateReferenceArguments(aMethod);
+        if (isObjectReturningMethod) {
+            localReferencesNeeded++;
+        }
+        wrapperMethodBodies.append("    if (env->PushLocalFrame(").append(localReferencesNeeded).append(") != 0) {\n" +
+                                   "        ALOG_BRIDGE(\"Exceptional exit of: %s\", __PRETTY_FUNCTION__);\n" +
+                                   "        env->ExceptionDescribe();\n"+
+                                   "        env->ExceptionClear();\n" +
+                                   "        return").append(Utils.getFailureReturnForType(returnType)).append(";\n" +
+                                   "    }\n\n");
+
+        // Marshall arguments, if we have any.
+        boolean hasArguments = argumentTypes.length != 0;
+
+        // We buffer the arguments to the call separately to avoid needing to repeatedly iterate the
+        // argument list while building this line. In the coming code block, we simultaneously
+        // construct any argument marshalling code (Creation of jstrings, placement of arguments
+        // into an argument array, etc. and the actual argument list passed to the function (in
+        // argumentContent).
+        StringBuilder argumentContent = new StringBuilder();
+        if (hasArguments) {
+            argumentContent.append(", ");
+            // If we have >2 arguments, use the jvalue[] calling approach.
+            if (argumentTypes.length > 2) {
+                wrapperMethodBodies.append("    jvalue args[").append(argumentTypes.length).append("];\n");
+                for (int aT = 0; aT < argumentTypes.length; aT++) {
+                    wrapperMethodBodies.append("    args[").append(aT).append("].");
+                    wrapperMethodBodies.append(Utils.getArrayArgumentMashallingLine(argumentTypes[aT], "a" + aT));
+                }
+
+                // The only argument is the array of arguments.
+                argumentContent.append("args");
+                wrapperMethodBodies.append('\n');
+            } else {
+                // Otherwise, use the vanilla calling approach.
+                boolean needsNewline = false;
+                for (int aT = 0; aT < argumentTypes.length; aT++) {
+                    // If the argument is a string-esque type, create a jstring from it, otherwise
+                    // it can be passed directly.
+                    if (Utils.isCharSequence(argumentTypes[aT])) {
+                        wrapperMethodBodies.append("    jstring j").append(aT).append(" = NewJavaString(env, a").append(aT).append(");\n");
+                        needsNewline = true;
+                        // Ensure we refer to the newly constructed Java string - not to the original
+                        // parameter to the wrapper function.
+                        argumentContent.append('j').append(aT);
+                    } else {
+                        argumentContent.append('a').append(aT);
+                    }
+                    if (aT != argumentTypes.length - 1) {
+                        argumentContent.append(", ");
+                    }
+                }
+                if (needsNewline) {
+                    wrapperMethodBodies.append('\n');
+                }
+            }
+        }
+
+        wrapperMethodBodies.append("    ");
+        if (!returnType.getCanonicalName().equals("void")) {
+            if (isObjectReturningMethod) {
+                wrapperMethodBodies.append("jobject");
+            } else {
+                wrapperMethodBodies.append(Utils.getCReturnType(returnType));
+            }
+            wrapperMethodBodies.append(" temp = ");
+        }
+
+        boolean isStaticJavaMethod = Utils.isMethodStatic(aMethod);
+
+        // The call into Java
+        wrapperMethodBodies.append("env->");
+        wrapperMethodBodies.append(Utils.getCallPrefix(returnType, isStaticJavaMethod));
+        if (argumentTypes.length > 2) {
+            wrapperMethodBodies.append('A');
+        }
+
+        wrapperMethodBodies.append('(');
+        // If the underlying Java method is nonstatic, we provide the target object to the JNI.
+        if (!isStaticJavaMethod) {
+            wrapperMethodBodies.append("aTarget, ");
+        } else {
+            // If the stub to be generated is static, we need to use the singleton to access the class
+            // reference.
+            if (aIsStaticBridgeMethod) {
+                wrapperMethodBodies.append("sBridge->");
+            }
+            // If this is a static underlyin Java method, we need to use the class reference in our
+            // call.
+            wrapperMethodBodies.append(Utils.getClassReferenceName(aClass)).append(", ");
+        }
+
+        // Write the method id out..
+        if (aIsStaticBridgeMethod) {
+            wrapperMethodBodies.append("sBridge->");
+        }
+        wrapperMethodBodies.append('j');
+        wrapperMethodBodies.append(aCMethodName);
+
+        // Tack on the arguments, if any..
+        wrapperMethodBodies.append(argumentContent);
+        wrapperMethodBodies.append(");\n\n");
+
+        // Check for exception and return the failure value..
+        wrapperMethodBodies.append("    if (env->ExceptionCheck()) {\n" +
+                                   "        ALOG_BRIDGE(\"Exceptional exit of: %s\", __PRETTY_FUNCTION__);\n" +
+                                   "        env->ExceptionDescribe();\n" +
+                                   "        env->ExceptionClear();\n" +
+                                   "        env->PopLocalFrame(NULL);\n" +
+                                   "        return").append(Utils.getFailureReturnForType(returnType)).append(";\n" +
+                                   "    }\n");
+
+        // If we're returning an object, pop the callee's stack frame extracting our ref as the return
+        // value.
+        if (isObjectReturningMethod) {
+            wrapperMethodBodies.append("    ");
+            wrapperMethodBodies.append(Utils.getCReturnType(returnType));
+            wrapperMethodBodies.append(" ret = static_cast<").append(Utils.getCReturnType(returnType)).append(">(env->PopLocalFrame(temp));\n" +
+                                       "    ALOG_BRIDGE(\"Exit of: %s\", __PRETTY_FUNCTION__);\n" +
+                                       "    return ret;\n");
+        } else if (!returnType.getCanonicalName().equals("void")) {
+            // If we're a primitive-returning function, just return the directly-obtained primative
+            // from the call to Java.
+            wrapperMethodBodies.append("    env->PopLocalFrame(NULL);\n" +
+                                       "    ALOG_BRIDGE(\"Exit of: %s\", __PRETTY_FUNCTION__);\n" +
+                                       "    return temp;\n");
+        } else {
+            // If we don't return anything, just pop the stack frame and move on with life.
+            wrapperMethodBodies.append("    ALOG_BRIDGE(\"Exit of: %s\", __PRETTY_FUNCTION__);\n" +
+                                       "    env->PopLocalFrame(NULL);\n");
+        }
+        wrapperMethodBodies.append("}\n");
+    }
+
+    /**
+     * Generates the code to get the method id of the given method on startup.
+     *
+     * @param aCMethodName    The C method name of the method being generated.
+     * @param aJavaMethodName The name of the Java method being wrapped.
+     * @param aMethod         The Java method being wrapped.
+     */
+    private void writeStartupCode(String aCMethodName, String aJavaMethodName, Method aMethod, Class<?> aClass) {
+        wrapperStartupCode.append("    j");
+        wrapperStartupCode.append(aCMethodName);
+        wrapperStartupCode.append(" = get");
+        if (Utils.isMethodStatic(aMethod)) {
+            wrapperStartupCode.append("Static");
+        }
+        wrapperStartupCode.append("Method(\"");
+        wrapperStartupCode.append(aJavaMethodName);
+        wrapperStartupCode.append("\", \"");
+        wrapperStartupCode.append(Utils.getTypeSignatureString(aMethod));
+        wrapperStartupCode.append("\");\n");
+    }
+
+    /**
+     * Create a method id field in the header file for the C method name provided.
+     *
+     * @param aMethodName C method name to generate a method id field for.
+     */
+    private void writeHeaderField(String aMethodName) {
+        headerFields.append("jmethodID j");
+        headerFields.append(aMethodName);
+        headerFields.append(";\n");
+    }
+
+    /**
+     * Get the finalised bytes to go into the generated wrappers file.
+     *
+     * @return The bytes to be written to the wrappers file.
+     */
+    public byte[] getWrapperFileContents() {
+        wrapperStartupCode.append("}\n");
+        wrapperStartupCode.append(wrapperMethodBodies);
+        wrapperMethodBodies.setLength(0);
+        return wrapperStartupCode.toString().getBytes();
+    }
+
+    /**
+     * Get the finalised bytes to go into the generated header file.
+     *
+     * @return The bytes to be written to the header file.
+     */
+    public byte[] getHeaderFileContents() {
+        headerFields.append('\n');
+        headerFields.append(headerMethods);
+        headerMethods.setLength(0);
+        return headerFields.toString().getBytes();
+    }
+}
new file mode 100644
--- /dev/null
+++ b/build/annotationProcessors/MethodWithAnnotationInfo.java
@@ -0,0 +1,24 @@
+/* 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/. */
+
+package org.mozilla.gecko.annotationProcessors;
+
+import java.lang.reflect.Method;
+
+/**
+ * Object holding method and annotation. Used by GeneratableEntryPointIterator.
+ */
+public class MethodWithAnnotationInfo {
+    public final Method method;
+    public final String wrapperName;
+    public final boolean isStatic;
+    public final boolean isMultithreaded;
+
+    public MethodWithAnnotationInfo(Method aMethod, String aWrapperName, boolean aIsStatic, boolean aIsMultithreaded) {
+        method = aMethod;
+        wrapperName = aWrapperName;
+        isStatic = aIsStatic;
+        isMultithreaded = aIsMultithreaded;
+    }
+}
new file mode 100644
--- /dev/null
+++ b/build/annotationProcessors/classloader/IterableJarLoadingURLClassLoader.java
@@ -0,0 +1,80 @@
+/* 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/. */
+
+package org.mozilla.gecko.annotationProcessors.classloader;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+/**
+ * A classloader which can be initialised with a list of jar files and which can provide an iterator
+ * over the top level classes in the jar files it was initialised with.
+ * classNames is kept sorted to ensure iteration order is consistent across program invocations.
+ * Otherwise, we'd forever be reporting the outdatedness of the generated code as we permute its
+ * contents.
+ * This classloader does not support inner classes. (You probably shouldn't be putting JNI entry
+ * points in inner classes anyway)
+ */
+public class IterableJarLoadingURLClassLoader extends URLClassLoader {
+    LinkedList<String> classNames = new LinkedList<String>();
+
+    /**
+     * Create an instance and return its iterator. Provides an iterator over the classes in the jar
+     * files provided as arguments.
+     * Inner classes are not supported.
+     *
+     * @param args A list of jar file names an iterator over the classes of which is desired.
+     * @return An iterator over the top level classes in the jar files provided, in arbitrary order.
+     */
+    public static Iterator<Class<?>> getIteratorOverJars(String[] args) {
+        URL[] urlArray = new URL[args.length];
+        LinkedList<String> aClassNames = new LinkedList<String>();
+
+        for (int i = 0; i < args.length; i++) {
+            try {
+                urlArray[i] = (new File(args[i])).toURI().toURL();
+
+                Enumeration<JarEntry> entries = new JarFile(args[i]).entries();
+                while (entries.hasMoreElements()) {
+                    JarEntry e = entries.nextElement();
+                    String fName = e.getName();
+                    if (!fName.endsWith(".class")) {
+                        continue;
+                    }
+                    final String className = fName.substring(0, fName.length() - 6).replace('/', '.');
+                    // Inner classes are not supported.
+                    if (className.contains("$")) {
+                        continue;
+                    }
+                    aClassNames.add(className);
+                }
+            } catch (IOException e) {
+                System.err.println("Error loading jar file \"" + args[i] + '"');
+                e.printStackTrace(System.err);
+            }
+        }
+        Collections.sort(aClassNames);
+        return new JarClassIterator(new IterableJarLoadingURLClassLoader(urlArray, aClassNames));
+    }
+
+    /**
+     * Constructs a classloader capable of loading all classes given as URLs in urls. Used by static
+     * method above.
+     *
+     * @param urls        URLs for all classes the new instance shall be capable of loading.
+     * @param aClassNames A list of names of the classes this instance shall be capable of loading.
+     */
+    protected IterableJarLoadingURLClassLoader(URL[] urls, LinkedList<String> aClassNames) {// Array to populate with URLs for each class in the given jars.
+        super(urls);
+        classNames = aClassNames;
+    }
+}
new file mode 100644
--- /dev/null
+++ b/build/annotationProcessors/classloader/JarClassIterator.java
@@ -0,0 +1,43 @@
+/* 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/. */
+
+package org.mozilla.gecko.annotationProcessors.classloader;
+
+import java.util.Iterator;
+
+/**
+ * Class for iterating over an IterableJarLoadingURLClassLoader's classes.
+ */
+public class JarClassIterator implements Iterator<Class<?>> {
+    private IterableJarLoadingURLClassLoader mTarget;
+    private Iterator<String> mTargetClassListIterator;
+
+    public JarClassIterator(IterableJarLoadingURLClassLoader aTarget) {
+        mTarget = aTarget;
+        mTargetClassListIterator = aTarget.classNames.iterator();
+    }
+
+    @Override
+    public boolean hasNext() {
+        return mTargetClassListIterator.hasNext();
+    }
+
+    @Override
+    public Class<?> next() {
+        String className = mTargetClassListIterator.next();
+        try {
+            return mTarget.loadClass(className);
+        } catch (ClassNotFoundException e) {
+            System.err.println("Unable to enumerate class: " + className + ". Corrupted jar file?");
+            e.printStackTrace();
+            System.exit(2);
+        }
+        return null;
+    }
+
+    @Override
+    public void remove() {
+        throw new UnsupportedOperationException("Removal of classes from iterator not supported.");
+    }
+}
new file mode 100644
--- /dev/null
+++ b/build/annotationProcessors/utils/AlphabeticMethodComparator.java
@@ -0,0 +1,28 @@
+/* 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/. */
+
+package org.mozilla.gecko.annotationProcessors.utils;
+
+import java.lang.reflect.Method;
+import java.util.Comparator;
+
+public class AlphabeticMethodComparator implements Comparator<Method> {
+    @Override
+    public int compare(Method lhs, Method rhs) {
+        // Initially, attempt to differentiate the methods be name alone..
+        String lName = lhs.getName();
+        String rName = rhs.getName();
+
+        int ret = lName.compareTo(rName);
+        if (ret != 0) {
+            return ret;
+        }
+
+        // The names were the same, so we need to compare signatures to find their uniqueness..
+        lName = Utils.getTypeSignatureString(lhs);
+        rName = Utils.getTypeSignatureString(rhs);
+
+        return lName.compareTo(rName);
+    }
+}
new file mode 100644
--- /dev/null
+++ b/build/annotationProcessors/utils/GeneratableEntryPointIterator.java
@@ -0,0 +1,111 @@
+/* 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/. */
+
+package org.mozilla.gecko.annotationProcessors.utils;
+
+import org.mozilla.gecko.annotationProcessors.MethodWithAnnotationInfo;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Iterator over the methods in a given method list which have the GeneratableAndroidBridgeTarget
+ * annotation. Returns an object containing both the annotation (Which may contain interesting
+ * parameters) and the argument.
+ */
+public class GeneratableEntryPointIterator implements Iterator<MethodWithAnnotationInfo> {
+    private final Method[] mMethods;
+    private MethodWithAnnotationInfo mNextReturnValue;
+    private int mMethodIndex;
+
+    public GeneratableEntryPointIterator(Method[] aMethods) {
+        // Sort the methods into alphabetical order by name, to ensure we always iterate methods
+        // in the same order..
+        Arrays.sort(aMethods, new AlphabeticMethodComparator());
+        mMethods = aMethods;
+
+        findNextValue();
+    }
+
+    /**
+     * Find and cache the next appropriately annotated method, plus the annotation parameter, if
+     * one exists. Otherwise cache null, so hasNext returns false.
+     */
+    private void findNextValue() {
+        while (mMethodIndex < mMethods.length) {
+            Method candidateMethod = mMethods[mMethodIndex];
+            mMethodIndex++;
+            for (Annotation annotation : candidateMethod.getDeclaredAnnotations()) {
+                // GeneratableAndroidBridgeTarget has a parameter. Use Reflection to obtain it.
+                Class<? extends Annotation> annotationType = annotation.annotationType();
+                final String annotationTypeName = annotationType.getName();
+
+                if (annotationTypeName.equals("org.mozilla.gecko.mozglue.GeneratableAndroidBridgeTarget")) {
+                    String stubName = null;
+                    boolean isStaticStub = false;
+                    boolean isMultithreadedStub = false;
+                    try {
+                        // Determine the explicitly-given name of the stub to generate, if any.
+                        final Method stubNameMethod = annotationType.getDeclaredMethod("stubName");
+                        stubNameMethod.setAccessible(true);
+                        stubName = (String) stubNameMethod.invoke(annotation);
+
+                        // Detemine if the generated stub should be static.
+                        final Method staticStubMethod = annotationType.getDeclaredMethod("generateStatic");
+                        staticStubMethod.setAccessible(true);
+                        isStaticStub = (Boolean) staticStubMethod.invoke(annotation);
+
+                        // Determine if the generated stub is to allow calls from multiple threads.
+                        final Method multithreadedStubMethod = annotationType.getDeclaredMethod("allowMultithread");
+                        multithreadedStubMethod.setAccessible(true);
+                        isMultithreadedStub = (Boolean) multithreadedStubMethod.invoke(annotation);
+                    } catch (NoSuchMethodException e) {
+                        System.err.println("Unable to find expected field on GeneratableAndroidBridgeTarget annotation. Did the signature change?");
+                        e.printStackTrace(System.err);
+                        System.exit(3);
+                    } catch (IllegalAccessException e) {
+                        System.err.println("IllegalAccessException reading fields on GeneratableAndroidBridgeTarget annotation. Seems the semantics of Reflection have changed...");
+                        e.printStackTrace(System.err);
+                        System.exit(4);
+                    } catch (InvocationTargetException e) {
+                        System.err.println("InvocationTargetException reading fields on GeneratableAndroidBridgeTarget annotation. This really shouldn't happen.");
+                        e.printStackTrace(System.err);
+                        System.exit(5);
+                    }
+                    // If the method name was not explicitly given in the annotation generate one...
+                    if (stubName.isEmpty()) {
+                        String aMethodName = candidateMethod.getName();
+                        stubName = aMethodName.substring(0, 1).toUpperCase() + aMethodName.substring(1);
+                    }
+
+                    mNextReturnValue = new MethodWithAnnotationInfo(candidateMethod, stubName, isStaticStub, isMultithreadedStub);
+                    return;
+                }
+            }
+        }
+        mNextReturnValue = null;
+    }
+
+    @Override
+    public boolean hasNext() {
+        return mNextReturnValue != null;
+    }
+
+    @Override
+    public MethodWithAnnotationInfo next() {
+        MethodWithAnnotationInfo ret = mNextReturnValue;
+        findNextValue();
+        return ret;
+    }
+
+    @Override
+    public void remove() {
+        throw new UnsupportedOperationException("Removal of methods from GeneratableEntryPointIterator not supported.");
+    }
+}
new file mode 100644
--- /dev/null
+++ b/build/annotationProcessors/utils/Utils.java
@@ -0,0 +1,530 @@
+/* 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/. */
+
+package org.mozilla.gecko.annotationProcessors.utils;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.HashMap;
+
+/**
+ * A collection of utility methods used by CodeGenerator. Largely used for translating types.
+ */
+public class Utils {
+
+    // A collection of lookup tables to simplify the functions to follow...
+    private static final HashMap<String, String> sBasicCTypes = new HashMap<String, String>();
+
+    static {
+        sBasicCTypes.put("void", "void");
+        sBasicCTypes.put("int", "int32_t");
+        sBasicCTypes.put("boolean", "bool");
+        sBasicCTypes.put("long", "int64_t");
+        sBasicCTypes.put("double", "jdouble");
+        sBasicCTypes.put("float", "jfloat");
+        sBasicCTypes.put("char", "uint16_t");
+        sBasicCTypes.put("byte", "int8_t");
+        sBasicCTypes.put("short", "int16_t");
+    }
+
+    private static final HashMap<String, String> sArrayCTypes = new HashMap<String, String>();
+
+    static {
+        sArrayCTypes.put("int", "jintArray");
+        sArrayCTypes.put("boolean", "jbooleanArray");
+        sArrayCTypes.put("long", "jlongArray");
+        sArrayCTypes.put("double", "jdoubleArray");
+        sArrayCTypes.put("float", "jfloatArray");
+        sArrayCTypes.put("char", "jcharArray");
+        sArrayCTypes.put("byte", "jbyteArray");
+        sArrayCTypes.put("short", "jshortArray");
+    }
+
+    private static final HashMap<String, String> sStaticCallTypes = new HashMap<String, String>();
+
+    static {
+        sStaticCallTypes.put("void", "CallStaticVoidMethod");
+        sStaticCallTypes.put("int", "CallStaticIntMethod");
+        sStaticCallTypes.put("boolean", "CallStaticBooleanMethod");
+        sStaticCallTypes.put("long", "CallStaticLongMethod");
+        sStaticCallTypes.put("double", "CallStaticDoubleMethod");
+        sStaticCallTypes.put("float", "CallStaticFloatMethod");
+        sStaticCallTypes.put("char", "CallStaticCharMethod");
+        sStaticCallTypes.put("byte", "CallStaticByteMethod");
+        sStaticCallTypes.put("short", "CallStaticShortMethod");
+    }
+
+    private static final HashMap<String, String> sInstanceCallTypes = new HashMap<String, String>();
+
+    static {
+        sInstanceCallTypes.put("void", "CallVoidMethod");
+        sInstanceCallTypes.put("int", "CallIntMethod");
+        sInstanceCallTypes.put("boolean", "CallBooleanMethod");
+        sInstanceCallTypes.put("long", "CallLongMethod");
+        sInstanceCallTypes.put("double", "CallDoubleMethod");
+        sInstanceCallTypes.put("float", "CallFloatMethod");
+        sInstanceCallTypes.put("char", "CallCharMethod");
+        sInstanceCallTypes.put("byte", "CallByteMethod");
+        sInstanceCallTypes.put("short", "CallShortMethod");
+    }
+
+    private static final HashMap<String, String> sFailureReturns = new HashMap<String, String>();
+
+    static {
+        sFailureReturns.put("void", "");
+        sFailureReturns.put("int", " 0");
+        sFailureReturns.put("boolean", " false");
+        sFailureReturns.put("long", " 0");
+        sFailureReturns.put("double", " 0.0");
+        sFailureReturns.put("float", " 0.0");
+        sFailureReturns.put("char", " 0");
+        sFailureReturns.put("byte", " 0");
+        sFailureReturns.put("short", " 0");
+    }
+
+    private static final HashMap<String, String> sCanonicalSignatureParts = new HashMap<String, String>();
+
+    static {
+        sCanonicalSignatureParts.put("void", "V");
+        sCanonicalSignatureParts.put("int", "I");
+        sCanonicalSignatureParts.put("boolean", "Z");
+        sCanonicalSignatureParts.put("long", "J");
+        sCanonicalSignatureParts.put("double", "D");
+        sCanonicalSignatureParts.put("float", "F");
+        sCanonicalSignatureParts.put("char", "C");
+        sCanonicalSignatureParts.put("byte", "B");
+        sCanonicalSignatureParts.put("short", "S");
+    }
+
+
+    private static final HashMap<String, String> sDefaultParameterValues = new HashMap<String, String>();
+
+    static {
+        sDefaultParameterValues.put("int", "0");
+        sDefaultParameterValues.put("boolean", "false");
+        sDefaultParameterValues.put("long", "0");
+        sDefaultParameterValues.put("double", "0");
+        sDefaultParameterValues.put("float", "0.0");
+        sDefaultParameterValues.put("char", "0");
+        sDefaultParameterValues.put("byte", "0");
+        sDefaultParameterValues.put("short", "0");
+    }
+
+    /**
+     * Get the C type corresponding to the provided type parameter. Used for generating argument
+     * types for the wrapper method.
+     *
+     * @param type Class to determine the corresponding JNI type for.
+     * @return true if the type an object type, false otherwise.
+     */
+    public static String getCParameterType(Class<?> type) {
+        String name = type.getCanonicalName();
+        if (sBasicCTypes.containsKey(name)) {
+            return sBasicCTypes.get(name);
+        }
+        // Are we dealing with an array type?
+        int len = name.length();
+        if (name.endsWith("[]")) {
+            // Determine if it is a 2D array - these map to jobjectArrays
+            name = name.substring(0, len - 2);
+            if (name.endsWith("[]")) {
+                return "jobjectArray";
+            } else {
+                // Which flavour of Array is it?
+                if (sArrayCTypes.containsKey(name)) {
+                    return sArrayCTypes.get(name);
+                }
+                return "jobjectArray";
+            }
+        }
+        // Not an array type, check the remaining possibilities before we fall back to jobject
+
+        // Check for CharSequences (Strings and things that are string-like)
+        if (isCharSequence(type)) {
+            return "const nsAString&";
+        }
+
+        if (name.equals("java.lang.Class")) {
+            // You're doing reflection on Java objects from inside C, returning Class objects
+            // to C, generating the corresponding code using this Java program. Really?!
+            return "jclass";
+        }
+        if (name.equals("java.lang.Throwable")) {
+            return "jthrowable";
+        }
+        return "jobject";
+    }
+
+    /**
+     * For a given Java type, get the corresponding C++ type if we're returning it from a function.
+     *
+     * @param type The Java return type.
+     * @return A string representation of the C++ return type.
+     */
+    public static String getCReturnType(Class<?> type) {
+        // Since there's only one thing we want to do differently...
+        String cParameterType = getCParameterType(type);
+        if (cParameterType.equals("const nsAString&")) {
+            return "jstring";
+        } else {
+            return cParameterType;
+        }
+    }
+
+    /**
+     * Gets the appropriate JNI call function to use to invoke a Java method with the given return
+     * type. This, plus a call postfix (Such as "A") forms a complete JNI call function name.
+     *
+     * @param aReturnType The Java return type of the method being generated.
+     * @param isStatic Boolean indicating if the underlying Java method is declared static.
+     * @return A string representation of the JNI call function prefix to use.
+     */
+    public static String getCallPrefix(Class<?> aReturnType, boolean isStatic) {
+        String name = aReturnType.getCanonicalName();
+        if (isStatic) {
+            if (sStaticCallTypes.containsKey(name)) {
+                return sStaticCallTypes.get(name);
+            }
+            return "CallStaticObjectMethod";
+        } else {
+            if (sInstanceCallTypes.containsKey(name)) {
+                return sInstanceCallTypes.get(name);
+            }
+            return "CallObjectMethod";
+        }
+    }
+
+    /**
+     * On failure, the generated method returns a null-esque value. This helper method gets the
+     * appropriate failure return value for a given Java return type, plus a leading space.
+     *
+     * @param type Java return type of method being generated
+     * @return String representation of the failure return value to be used in the generated code.
+     */
+    public static String getFailureReturnForType(Class<?> type) {
+        String name = type.getCanonicalName();
+        if (sFailureReturns.containsKey(name)) {
+            return sFailureReturns.get(name);
+        }
+        return " nullptr";
+    }
+
+    /**
+     * Get the canonical JNI type signature for a method.
+     *
+     * @param aMethod The method to generate a signature for.
+     * @return The canonical JNI type signature for this method.
+     */
+    public static String getTypeSignatureString(Method aMethod) {
+        Class<?>[] arguments = aMethod.getParameterTypes();
+        Class<?> returnType = aMethod.getReturnType();
+
+        StringBuilder sb = new StringBuilder();
+        sb.append('(');
+        // For each argument, write its signature component to the buffer..
+        for (int i = 0; i < arguments.length; i++) {
+            writeTypeSignature(sb, arguments[i]);
+        }
+        sb.append(')');
+        // Write the return value's signature..
+        writeTypeSignature(sb, returnType);
+        return sb.toString();
+    }
+
+    /**
+     * Helper method used by getTypeSignatureString to build the signature. Write the subsignature
+     * of a given type into the buffer.
+     *
+     * @param sb The buffer to write into.
+     * @param c  The type of the element to write the subsignature of.
+     */
+    private static void writeTypeSignature(StringBuilder sb, Class<?> c) {
+        String name = c.getCanonicalName().replaceAll("\\.", "/");
+        // Determine if this is an array type and, if so, peel away the array operators..
+        int len = name.length();
+        while (name.endsWith("[]")) {
+            sb.append('[');
+            name = name.substring(0, len - 2);
+        }
+
+        // Look in the hashmap for the remainder...
+        if (sCanonicalSignatureParts.containsKey(name)) {
+            // It was a primitive type, so lookup was a success.
+            sb.append(sCanonicalSignatureParts.get(name));
+        } else {
+            // It was a reference type - generate.
+            sb.append('L');
+            sb.append(name);
+            sb.append(';');
+        }
+    }
+
+    /**
+     * Produces a C method signature, sans semicolon, for the given Java Method. Useful for both
+     * generating header files and method bodies.
+     *
+     * @param aMethod      The Java method to generate the corresponding wrapper signature for.
+     * @param aCMethodName The name of the generated method this is to be the signatgure for.
+     * @return The generated method signature.
+     */
+    public static String getCImplementationMethodSignature(Method aMethod, String aCMethodName) {
+        Class<?>[] argumentTypes = aMethod.getParameterTypes();
+        Class<?> returnType = aMethod.getReturnType();
+
+        StringBuilder retBuffer = new StringBuilder();
+        // Write return type..
+        retBuffer.append(getCReturnType(returnType));
+        retBuffer.append(" AndroidBridge::");
+        retBuffer.append(aCMethodName);
+        retBuffer.append('(');
+
+        // For an instance method, the first argument is the target object.
+        if (!isMethodStatic(aMethod)) {
+            retBuffer.append("jobject aTarget");
+            if (argumentTypes.length > 0) {
+                retBuffer.append(", ");
+            }
+        }
+
+        // Write argument types...
+        for (int aT = 0; aT < argumentTypes.length; aT++) {
+            retBuffer.append(getCParameterType(argumentTypes[aT]));
+            retBuffer.append(" a");
+            // We, imaginatively, call our arguments a1, a2, a3...
+            // The only way to preserve the names from Java would be to parse the
+            // Java source, which would be computationally hard.
+            retBuffer.append(aT);
+            if (aT != argumentTypes.length - 1) {
+                retBuffer.append(", ");
+            }
+        }
+        retBuffer.append(')');
+        return retBuffer.toString();
+    }
+
+    /**
+     * Produces a C method signature, sans semicolon, for the given Java Method. Useful for both
+     * generating header files and method bodies.
+     *
+     * @param aMethod      The Java method to generate the corresponding wrapper signature for.
+     * @param aCMethodName The name of the generated method this is to be the signatgure for.
+     * @return The generated method signature.
+     */
+    public static String getCHeaderMethodSignature(Method aMethod, String aCMethodName, boolean aIsStaticStub) {
+        Class<?>[] argumentTypes = aMethod.getParameterTypes();
+
+        // The annotations on the parameters of this method, in declaration order.
+        // Importantly - the same order as those in argumentTypes.
+        Annotation[][] argumentAnnotations = aMethod.getParameterAnnotations();
+        Class<?> returnType = aMethod.getReturnType();
+
+        StringBuilder retBuffer = new StringBuilder();
+
+        // Add the static keyword, if applicable.
+        if (aIsStaticStub) {
+            retBuffer.append("static ");
+        }
+
+        // Write return type..
+        retBuffer.append(getCReturnType(returnType));
+        retBuffer.append(' ');
+        retBuffer.append(aCMethodName);
+        retBuffer.append('(');
+
+        // For an instance method, the first argument is the target object.
+        if (!isMethodStatic(aMethod)) {
+            retBuffer.append("jobject aTarget");
+            if (argumentTypes.length > 0) {
+                retBuffer.append(", ");
+            }
+        }
+
+        // Write argument types...
+        for (int aT = 0; aT < argumentTypes.length; aT++) {
+            retBuffer.append(getCParameterType(argumentTypes[aT]));
+            retBuffer.append(" a");
+            // We, imaginatively, call our arguments a1, a2, a3...
+            // The only way to preserve the names from Java would be to parse the
+            // Java source, which would be computationally hard.
+            retBuffer.append(aT);
+
+            // Append the default value, if there is one..
+            retBuffer.append(getDefaultValueString(argumentTypes[aT], argumentAnnotations[aT]));
+
+            if (aT != argumentTypes.length - 1) {
+                retBuffer.append(", ");
+            }
+        }
+        retBuffer.append(')');
+        return retBuffer.toString();
+    }
+
+    /**
+     * If the given Annotation[] contains an OptionalGeneratedParameter annotation then return a
+     * string assigning an argument of type aArgumentType to the default value for that type.
+     * Otherwise, return the empty string.
+     *
+     * @param aArgumentType        The type of the argument to consider.
+     * @param aArgumentAnnotations The annotations on the argument to consider.
+     * @return An appropriate string to append to the signature of this argument assigning it to a
+     *         default value (Or not, as applicable).
+     */
+    public static String getDefaultValueString(Class<?> aArgumentType, Annotation[] aArgumentAnnotations) {
+        for (int i = 0; i < aArgumentAnnotations.length; i++) {
+            Class<? extends Annotation> annotationType = aArgumentAnnotations[i].annotationType();
+            final String annotationTypeName = annotationType.getName();
+            if (annotationTypeName.equals("org.mozilla.gecko.mozglue.OptionalGeneratedParameter")) {
+                return " = " + getDefaultParameterValueForType(aArgumentType);
+            }
+        }
+        return "";
+    }
+
+    /**
+     * Helper method to return an appropriate default parameter value for an argument of a given type.
+     * The lookup table contains values for primitive types and strings. All other object types default
+     * to null pointers.
+     *
+     * @param aArgumentType The parameter type for which a default value is desired.
+     * @return An appropriate string representation of the default value selected, for use in generated
+     *         C++ code.
+     */
+    private static String getDefaultParameterValueForType(Class<?> aArgumentType) {
+        String typeName = aArgumentType.getCanonicalName();
+        if (sDefaultParameterValues.containsKey(typeName)) {
+            return sDefaultParameterValues.get(typeName);
+        } else if (isCharSequence(aArgumentType)) {
+            return "EmptyString()";
+        } else {
+            return "nullptr";
+        }
+    }
+
+    /**
+     * Helper method that returns the number of reference types in the arguments of m.
+     *
+     * @param m The method to consider.
+     * @return How many of the arguments of m are nonprimitive.
+     */
+    public static int enumerateReferenceArguments(Method m) {
+        int ret = 0;
+        Class<?>[] args = m.getParameterTypes();
+        for (int i = 0; i < args.length; i++) {
+            String name = args[i].getCanonicalName();
+            if (!sBasicCTypes.containsKey(name)) {
+                ret++;
+            }
+        }
+        return ret;
+    }
+
+    /**
+     * Helper method that returns true iff the given method has a string argument.
+     *
+     * @param m The method to consider.
+     * @return True if the given method has a string argument, false otherwise.
+     */
+    public static boolean hasStringArgument(Method m) {
+        Class<?>[] args = m.getParameterTypes();
+        for (int i = 0; i < args.length; i++) {
+            if (isCharSequence(args[i])) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Write the argument array assignment line for the given argument type. Does not support array
+     * types.
+     *
+     * @param type    Type of this argument according to the target Java method's signature.
+     * @param argName Wrapper function argument name corresponding to this argument.
+     */
+    public static String getArrayArgumentMashallingLine(Class<?> type, String argName) {
+        StringBuilder sb = new StringBuilder();
+
+        String name = type.getCanonicalName();
+        if (sCanonicalSignatureParts.containsKey(name)) {
+            sb.append(sCanonicalSignatureParts.get(name).toLowerCase());
+            sb.append(" = ").append(argName).append(";\n");
+        } else {
+            if (isCharSequence(type)) {
+                sb.append("l = NewJavaString(env, ").append(argName).append(");\n");
+            } else {
+                sb.append("l = ").append(argName).append(";\n");
+            }
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * Returns true if the method provided returns an object type. Returns false if it returns a
+     * primitive type.
+     *
+     * @param aMethod The method to consider.
+     * @return true if the method provided returns an object type, false otherwise.
+     */
+    public static boolean doesReturnObjectType(Method aMethod) {
+        Class<?> returnType = aMethod.getReturnType();
+        return !sBasicCTypes.containsKey(returnType.getCanonicalName());
+    }
+
+    /**
+     * For a given Java class, get the name of the value in C++ which holds a reference to it.
+     *
+     * @param aClass Target Java class.
+     * @return The name of the C++ jclass entity referencing the given class.
+     */
+    public static String getClassReferenceName(Class<?> aClass) {
+        String className = aClass.getSimpleName();
+        return 'm' + className + "Class";
+    }
+
+    /**
+     * Generate a line to get a global reference to the Java class given.
+     *
+     * @param aClass The target Java class.
+     * @return The generated code to populate the reference to the class.
+     */
+    public static String getStartupLineForClass(Class<?> aClass) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("    ");
+        sb.append(getClassReferenceName(aClass));
+        sb.append(" = getClassGlobalRef(\"");
+        sb.append(aClass.getCanonicalName().replaceAll("\\.", "/"));
+        sb.append("\");\n");
+        return sb.toString();
+    }
+
+    /**
+     * Helper method to determine if this object implements CharSequence
+     * @param aClass Class to check for CharSequence-esqueness
+     * @return True if the given class implements CharSequence, false otherwise.
+     */
+    public static boolean isCharSequence(Class aClass) {
+        if (aClass.getCanonicalName().equals("java.lang.CharSequence")) {
+            return true;
+        }
+        Class[] interfaces = aClass.getInterfaces();
+        for (Class c : interfaces) {
+            if (c.getCanonicalName().equals("java.lang.CharSequence")) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Helper method to read the modifier bits of the given method to determine if it is static.
+     * @param aMethod The Method to check.
+     * @return true of the method is declared static, false otherwise.
+     */
+    public static boolean isMethodStatic(Method aMethod) {
+        int aMethodModifiers = aMethod.getModifiers();
+        return Modifier.isStatic(aMethodModifiers);
+    }
+}
--- a/content/canvas/public/nsICanvasRenderingContextInternal.h
+++ b/content/canvas/public/nsICanvasRenderingContextInternal.h
@@ -9,18 +9,18 @@
 #include "nsISupports.h"
 #include "nsIInputStream.h"
 #include "nsIDocShell.h"
 #include "mozilla/dom/HTMLCanvasElement.h"
 #include "gfxPattern.h"
 #include "mozilla/RefPtr.h"
 
 #define NS_ICANVASRENDERINGCONTEXTINTERNAL_IID \
-{ 0x9a6a5bdf, 0x1261, 0x4057, \
-  { 0x85, 0xcc, 0xaf, 0x97, 0x6c, 0x36, 0x99, 0xa9 } }
+{ 0x8b8da863, 0xd151, 0x4014, \
+  { 0x8b, 0xdc, 0x62, 0xb5, 0x0d, 0xc0, 0x2b, 0x62 } }
 
 class gfxContext;
 class gfxASurface;
 class nsDisplayListBuilder;
 
 namespace mozilla {
 namespace layers {
 class CanvasLayer;
@@ -60,19 +60,16 @@ public:
 
   NS_IMETHOD InitializeWithSurface(nsIDocShell *docShell, gfxASurface *surface, int32_t width, int32_t height) = 0;
 
   // Render the canvas at the origin of the given gfxContext
   NS_IMETHOD Render(gfxContext *ctx,
                     gfxPattern::GraphicsFilter aFilter,
                     uint32_t aFlags = RenderFlagPremultAlpha) = 0;
 
-  // Creates an image buffer. Returns null on failure.
-  virtual void GetImageBuffer(uint8_t** aImageBuffer, int32_t* aFormat) = 0;
-
   // Gives you a stream containing the image represented by this context.
   // The format is given in aMimeTime, for example "image/png".
   //
   // If the image format does not support transparency or aIncludeTransparency
   // is false, alpha will be discarded and the result will be the image
   // composited on black.
   NS_IMETHOD GetInputStream(const char *aMimeType,
                             const PRUnichar *aEncoderOptions,
--- a/content/canvas/src/CanvasRenderingContext2D.cpp
+++ b/content/canvas/src/CanvasRenderingContext2D.cpp
@@ -43,17 +43,17 @@
 #include "nsIDocShell.h"
 #include "nsIDOMWindow.h"
 #include "nsPIDOMWindow.h"
 #include "nsIXPConnect.h"
 #include "nsDisplayList.h"
 
 #include "nsTArray.h"
 
-#include "ImageEncoder.h"
+#include "imgIEncoder.h"
 
 #include "gfxContext.h"
 #include "gfxASurface.h"
 #include "gfxImageSurface.h"
 #include "gfxPlatform.h"
 #include "gfxFont.h"
 #include "gfxBlur.h"
 #include "gfxUtils.h"
@@ -1041,81 +1041,81 @@ CanvasRenderingContext2D::Render(gfxCont
       NS_ABORT_IF_FALSE(gis, "If non-premult alpha, must be able to get image surface!");
 
       gfxUtils::UnpremultiplyImageSurface(gis);
   }
 
   return rv;
 }
 
-void
-CanvasRenderingContext2D::GetImageBuffer(uint8_t** aImageBuffer,
-                                         int32_t* aFormat)
+NS_IMETHODIMP
+CanvasRenderingContext2D::GetInputStream(const char *aMimeType,
+                                         const PRUnichar *aEncoderOptions,
+                                         nsIInputStream **aStream)
 {
-  *aImageBuffer = nullptr;
-  *aFormat = 0;
+  EnsureTarget();
+  if (!IsTargetValid()) {
+    return NS_ERROR_FAILURE;
+  }
 
   nsRefPtr<gfxASurface> surface;
-  nsresult rv = GetThebesSurface(getter_AddRefs(surface));
-  if (NS_FAILED(rv)) {
-    return;
+
+  if (NS_FAILED(GetThebesSurface(getter_AddRefs(surface)))) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsresult rv;
+  const char encoderPrefix[] = "@mozilla.org/image/encoder;2?type=";
+  static const fallible_t fallible = fallible_t();
+  nsAutoArrayPtr<char> conid(new (fallible) char[strlen(encoderPrefix) + strlen(aMimeType) + 1]);
+
+  if (!conid) {
+    return NS_ERROR_OUT_OF_MEMORY;
   }
 
-  static const fallible_t fallible = fallible_t();
-  uint8_t* imageBuffer = new (fallible) uint8_t[mWidth * mHeight * 4];
+  strcpy(conid, encoderPrefix);
+  strcat(conid, aMimeType);
+
+  nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(conid);
+  if (!encoder) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsAutoArrayPtr<uint8_t> imageBuffer(new (fallible) uint8_t[mWidth * mHeight * 4]);
   if (!imageBuffer) {
-    return;
+    return NS_ERROR_OUT_OF_MEMORY;
   }
 
   nsRefPtr<gfxImageSurface> imgsurf =
-    new gfxImageSurface(imageBuffer,
+    new gfxImageSurface(imageBuffer.get(),
                         gfxIntSize(mWidth, mHeight),
                         mWidth * 4,
                         gfxASurface::ImageFormatARGB32);
 
   if (!imgsurf || imgsurf->CairoStatus()) {
-    delete[] imageBuffer;
-    return;
+    return NS_ERROR_FAILURE;
   }
 
   nsRefPtr<gfxContext> ctx = new gfxContext(imgsurf);
+
   if (!ctx || ctx->HasError()) {
-    delete[] imageBuffer;
-    return;
+    return NS_ERROR_FAILURE;
   }
 
   ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
   ctx->SetSource(surface, gfxPoint(0, 0));
   ctx->Paint();
 
-  *aImageBuffer = imageBuffer;
-  *aFormat = imgIEncoder::INPUT_FORMAT_HOSTARGB;
-}
-
-NS_IMETHODIMP
-CanvasRenderingContext2D::GetInputStream(const char *aMimeType,
-                                         const PRUnichar *aEncoderOptions,
-                                         nsIInputStream **aStream)
-{
-  uint8_t* imageBuffer = nullptr;
-  int32_t format = 0;
-  GetImageBuffer(&imageBuffer, &format);
-  if (!imageBuffer) {
-    return NS_ERROR_FAILURE;
-  }
-
-  nsCString enccid("@mozilla.org/image/encoder;2?type=");
-  enccid += aMimeType;
-  nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(enccid.get());
-  if (!encoder) {
-    return NS_ERROR_FAILURE;
-  }
-
-  return ImageEncoder::GetInputStream(mWidth, mHeight, imageBuffer, format,
-                                      encoder, aEncoderOptions, aStream);
+  rv = encoder->InitFromData(imageBuffer.get(),
+                              mWidth * mHeight * 4, mWidth, mHeight, mWidth * 4,
+                              imgIEncoder::INPUT_FORMAT_HOSTARGB,
+                              nsDependentString(aEncoderOptions));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return CallQueryInterface(encoder, aStream);
 }
 
 SurfaceFormat
 CanvasRenderingContext2D::GetSurfaceFormat() const
 {
   return mOpaque ? FORMAT_B8G8R8X8 : FORMAT_B8G8R8A8;
 }
 
@@ -3744,19 +3744,16 @@ CanvasRenderingContext2D::PutImageData_e
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 CanvasRenderingContext2D::GetThebesSurface(gfxASurface **surface)
 {
   EnsureTarget();
-  if (!IsTargetValid()) {
-    return NS_ERROR_FAILURE;
-  }
 
   nsRefPtr<gfxASurface> thebesSurface =
       gfxPlatform::GetPlatform()->GetThebesSurfaceForDrawTarget(mTarget);
 
   if (!thebesSurface) {
     return NS_ERROR_FAILURE;
   }
 
--- a/content/canvas/src/CanvasRenderingContext2D.h
+++ b/content/canvas/src/CanvasRenderingContext2D.h
@@ -15,17 +15,16 @@
 #include "mozilla/dom/HTMLVideoElement.h"
 #include "CanvasUtils.h"
 #include "gfxFont.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/CanvasGradient.h"
 #include "mozilla/dom/CanvasRenderingContext2DBinding.h"
 #include "mozilla/dom/CanvasPattern.h"
 #include "mozilla/gfx/Rect.h"
-#include "imgIEncoder.h"
 
 class nsXULElement;
 
 namespace mozilla {
 namespace gfx {
 class SourceSurface;
 }
 
@@ -444,18 +443,16 @@ public:
       mDSPathBuilder->BezierTo(transform * aCP1,
                                 transform * aCP2,
                                 transform * aCP3);
     }
   }
 
   friend class CanvasRenderingContext2DUserData;
 
-  virtual void GetImageBuffer(uint8_t** aImageBuffer, int32_t* aFormat);
-
 protected:
   nsresult GetImageDataArray(JSContext* aCx, int32_t aX, int32_t aY,
                              uint32_t aWidth, uint32_t aHeight,
                              JSObject** aRetval);
 
   nsresult PutImageData_explicit(int32_t x, int32_t y, uint32_t w, uint32_t h,
                                  unsigned char *aData, uint32_t aDataLen,
                                  bool hasDirtyRect, int32_t dirtyX, int32_t dirtyY,
deleted file mode 100644
--- a/content/canvas/src/ImageEncoder.cpp
+++ /dev/null
@@ -1,296 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* 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 "ImageEncoder.h"
-
-#include "mozilla/dom/CanvasRenderingContext2D.h"
-
-namespace mozilla {
-namespace dom {
-
-class EncodingCompleteEvent : public nsRunnable
-{
-public:
-  NS_DECL_THREADSAFE_ISUPPORTS
-
-  EncodingCompleteEvent(JSContext* aJSContext,
-                        nsIThread* aEncoderThread,
-                        nsIFileCallback* aCallback)
-    : mImgSize(0)
-    , mType()
-    , mImgData(nullptr)
-    , mJSContext(aJSContext)
-    , mEncoderThread(aEncoderThread)
-    , mCallback(aCallback)
-  {}
-  virtual ~EncodingCompleteEvent() {}
-
-  NS_IMETHOD Run()
-  {
-    nsRefPtr<nsDOMMemoryFile> blob =
-      new nsDOMMemoryFile(mImgData, mImgSize, mType);
-
-    if (mJSContext) {
-      JS_updateMallocCounter(mJSContext, mImgSize);
-    }
-    nsresult rv = mCallback->Receive(blob);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    mEncoderThread->Shutdown();
-    return rv;
-  }
-
-  void SetMembers(void* aImgData, uint64_t aImgSize, const nsAutoString& aType)
-  {
-    mImgData = aImgData;
-    mImgSize = aImgSize;
-    mType = aType;
-  }
-
-private:
-  uint64_t mImgSize;
-  nsAutoString mType;
-  void* mImgData;
-  JSContext* mJSContext;
-  nsCOMPtr<nsIThread> mEncoderThread;
-  nsCOMPtr<nsIFileCallback> mCallback;
-};
-
-NS_IMPL_ISUPPORTS1(EncodingCompleteEvent, nsIRunnable);
-
-class EncodingRunnable : public nsRunnable
-{
-public:
-  NS_DECL_THREADSAFE_ISUPPORTS
-
-  EncodingRunnable(const nsAString& aType,
-                   const nsAString& aOptions,
-                   uint8_t* aImageBuffer,
-                   imgIEncoder* aEncoder,
-                   nsIThread* aOriginThread,
-                   EncodingCompleteEvent* aEncodingCompleteEvent,
-                   int32_t aFormat,
-                   const nsIntSize aSize,
-                   bool aUsingCustomOptions)
-    : mType(aType)
-    , mOptions(aOptions)
-    , mImageBuffer(aImageBuffer)
-    , mEncoder(aEncoder)
-    , mOriginThread(aOriginThread)
-    , mEncodingCompleteEvent(aEncodingCompleteEvent)
-    , mFormat(aFormat)
-    , mSize(aSize)
-    , mUsingCustomOptions(aUsingCustomOptions)
-  {}
-  virtual ~EncodingRunnable() {}
-
-  NS_IMETHOD Run()
-  {
-    nsCOMPtr<nsIInputStream> stream;
-    nsresult rv = ImageEncoder::ExtractDataInternal(mType,
-                                                    mOptions,
-                                                    mImageBuffer,
-                                                    mFormat,
-                                                    mSize,
-                                                    nullptr,
-                                                    getter_AddRefs(stream),
-                                                    mEncoder);
-
-    // If there are unrecognized custom parse options, we should fall back to
-    // the default values for the encoder without any options at all.
-    if (rv == NS_ERROR_INVALID_ARG && mUsingCustomOptions) {
-      rv = ImageEncoder::ExtractDataInternal(mType,
-                                             EmptyString(),
-                                             mImageBuffer,
-                                             mFormat,
-                                             mSize,
-                                             nullptr,
-                                             getter_AddRefs(stream),
-                                             mEncoder);
-    }
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    uint64_t imgSize;
-    rv = stream->Available(&imgSize);
-    NS_ENSURE_SUCCESS(rv, rv);
-    NS_ENSURE_TRUE(imgSize <= UINT32_MAX, NS_ERROR_FILE_TOO_BIG);
-
-    void* imgData = nullptr;
-    rv = NS_ReadInputStreamToBuffer(stream, &imgData, imgSize);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    mEncodingCompleteEvent->SetMembers(imgData, imgSize, mType);
-    rv = mOriginThread->Dispatch(mEncodingCompleteEvent, NS_DISPATCH_NORMAL);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    return rv;
-  }
-
-private:
-  nsAutoString mType;
-  nsAutoString mOptions;
-  nsAutoArrayPtr<uint8_t> mImageBuffer;
-  nsCOMPtr<imgIEncoder> mEncoder;
-  nsCOMPtr<nsIThread> mOriginThread;
-  nsRefPtr<EncodingCompleteEvent> mEncodingCompleteEvent;
-  int32_t mFormat;
-  const nsIntSize mSize;
-  bool mUsingCustomOptions;
-};
-
-NS_IMPL_ISUPPORTS1(EncodingRunnable, nsIRunnable)
-
-/* static */
-nsresult
-ImageEncoder::ExtractData(nsAString& aType,
-                          const nsAString& aOptions,
-                          const nsIntSize aSize,
-                          nsICanvasRenderingContextInternal* aContext,
-                          nsIInputStream** aStream)
-{
-  nsCOMPtr<imgIEncoder> encoder = ImageEncoder::GetImageEncoder(aType);
-  if (!encoder) {
-    return NS_IMAGELIB_ERROR_NO_ENCODER;
-  }
-
-  return ExtractDataInternal(aType, aOptions, nullptr, 0, aSize, aContext,
-                             aStream, encoder);
-}
-
-/* static */
-nsresult
-ImageEncoder::ExtractDataAsync(nsAString& aType,
-                               const nsAString& aOptions,
-                               bool aUsingCustomOptions,
-                               uint8_t* aImageBuffer,
-                               int32_t aFormat,
-                               const nsIntSize aSize,
-                               nsICanvasRenderingContextInternal* aContext,
-                               JSContext* aJSContext,
-                               nsIFileCallback* aCallback)
-{
-  nsCOMPtr<imgIEncoder> encoder = ImageEncoder::GetImageEncoder(aType);
-  if (!encoder) {
-    return NS_IMAGELIB_ERROR_NO_ENCODER;
-  }
-
-  nsCOMPtr<nsIThread> encoderThread;
-  nsresult rv = NS_NewThread(getter_AddRefs(encoderThread), nullptr);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsRefPtr<EncodingCompleteEvent> completeEvent =
-    new EncodingCompleteEvent(aJSContext, encoderThread, aCallback);
-
-  nsCOMPtr<nsIRunnable> event = new EncodingRunnable(aType,
-                                                     aOptions,
-                                                     aImageBuffer,
-                                                     encoder,
-                                                     NS_GetCurrentThread(),
-                                                     completeEvent,
-                                                     aFormat,
-                                                     aSize,
-                                                     aUsingCustomOptions);
-  return encoderThread->Dispatch(event, NS_DISPATCH_NORMAL);
-}
-
-/*static*/ nsresult
-ImageEncoder::GetInputStream(int32_t aWidth,
-                             int32_t aHeight,
-                             uint8_t* aImageBuffer,
-                             int32_t aFormat,
-                             imgIEncoder* aEncoder,
-                             const PRUnichar* aEncoderOptions,
-                             nsIInputStream** aStream)
-{
-  nsresult rv =
-    aEncoder->InitFromData(aImageBuffer,
-                           aWidth * aHeight * 4, aWidth, aHeight, aWidth * 4,
-                           aFormat,
-                           nsDependentString(aEncoderOptions));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return CallQueryInterface(aEncoder, aStream);
-}
-
-/* static */
-nsresult
-ImageEncoder::ExtractDataInternal(const nsAString& aType,
-                                  const nsAString& aOptions,
-                                  uint8_t* aImageBuffer,
-                                  int32_t aFormat,
-                                  const nsIntSize aSize,
-                                  nsICanvasRenderingContextInternal* aContext,
-                                  nsIInputStream** aStream,
-                                  imgIEncoder* aEncoder)
-{
-  nsCOMPtr<nsIInputStream> imgStream;
-
-  // get image bytes
-  nsresult rv;
-  if (aImageBuffer) {
-    rv = ImageEncoder::GetInputStream(
-      aSize.width,
-      aSize.height,
-      aImageBuffer,
-      aFormat,
-      aEncoder,
-      nsPromiseFlatString(aOptions).get(),
-      getter_AddRefs(imgStream));
-  } else if (aContext) {
-    NS_ConvertUTF16toUTF8 encoderType(aType);
-    rv = aContext->GetInputStream(encoderType.get(),
-                                  nsPromiseFlatString(aOptions).get(),
-                                  getter_AddRefs(imgStream));
-  } else {
-    // no context, so we have to encode an empty image
-    // note that if we didn't have a current context, the spec says we're
-    // supposed to just return transparent black pixels of the canvas
-    // dimensions.
-    nsRefPtr<gfxImageSurface> emptyCanvas =
-      new gfxImageSurface(gfxIntSize(aSize.width, aSize.height),
-                          gfxASurface::ImageFormatARGB32);
-    if (emptyCanvas->CairoStatus()) {
-      return NS_ERROR_INVALID_ARG;
-    }
-    rv = aEncoder->InitFromData(emptyCanvas->Data(),
-                                aSize.width * aSize.height * 4,
-                                aSize.width,
-                                aSize.height,
-                                aSize.width * 4,
-                                imgIEncoder::INPUT_FORMAT_HOSTARGB,
-                                aOptions);
-    if (NS_SUCCEEDED(rv)) {
-      imgStream = do_QueryInterface(aEncoder);
-    }
-  }
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  imgStream.forget(aStream);
-  return rv;
-}
-
-/* static */
-already_AddRefed<imgIEncoder>
-ImageEncoder::GetImageEncoder(nsAString& aType)
-{
-  // Get an image encoder for the media type.
-  nsCString encoderCID("@mozilla.org/image/encoder;2?type=");
-  NS_ConvertUTF16toUTF8 encoderType(aType);
-  encoderCID += encoderType;
-  nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(encoderCID.get());
-
-  if (!encoder && aType != NS_LITERAL_STRING("image/png")) {
-    // Unable to create an encoder instance of the specified type. Falling back
-    // to PNG.
-    aType.AssignLiteral("image/png");
-    nsCString PNGEncoderCID("@mozilla.org/image/encoder;2?type=image/png");
-    encoder = do_CreateInstance(PNGEncoderCID.get());
-  }
-
-  return encoder.forget();
-}
-
-} // namespace dom
-} // namespace mozilla
deleted file mode 100644
--- a/content/canvas/src/ImageEncoder.h
+++ /dev/null
@@ -1,92 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* 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 ImageEncoder_h
-#define ImageEncoder_h
-
-#include "imgIEncoder.h"
-#include "nsDOMFile.h"
-#include "nsError.h"
-#include "nsIDOMHTMLCanvasElement.h"
-#include "nsLayoutUtils.h"
-#include "nsNetUtil.h"
-#include "nsSize.h"
-
-class nsICanvasRenderingContextInternal;
-
-namespace mozilla {
-namespace dom {
-
-class EncodingRunnable;
-
-class ImageEncoder
-{
-public:
-  // Extracts data synchronously and gives you a stream containing the image
-  // represented by aContext. aType may change to "image/png" if we had to fall
-  // back to a PNG encoder. A return value of NS_OK implies successful data
-  // extraction. If there are any unrecognized custom parse options in
-  // aOptions, NS_ERROR_INVALID_ARG will be returned. When encountering this
-  // error it is usual to call this function again without any options at all.
-  static nsresult ExtractData(nsAString& aType,
-                              const nsAString& aOptions,
-                              const nsIntSize aSize,
-                              nsICanvasRenderingContextInternal* aContext,
-                              nsIInputStream** aStream);
-
-  // Extracts data asynchronously. aType may change to "image/png" if we had to
-  // fall back to a PNG encoder. aOptions are the options to be passed to the
-  // encoder and aUsingCustomOptions specifies whether custom parse options were
-  // used (i.e. by using -moz-parse-options). If there are any unrecognized
-  // custom parse options, we fall back to the default values for the encoder
-  // without any options at all. A return value of NS_OK only implies
-  // successful dispatching of the extraction step to the encoding thread.
-  static nsresult ExtractDataAsync(nsAString& aType,
-                                   const nsAString& aOptions,
-                                   bool aUsingCustomOptions,
-                                   uint8_t* aImageBuffer,
-                                   int32_t aFormat,
-                                   const nsIntSize aSize,
-                                   nsICanvasRenderingContextInternal* aContext,
-                                   JSContext* aJSContext,
-                                   nsIFileCallback* aCallback);
-
-  // Gives you a stream containing the image represented by aImageBuffer.
-  // The format is given in aFormat, for example
-  // imgIEncoder::INPUT_FORMAT_HOSTARGB.
-  static nsresult GetInputStream(int32_t aWidth,
-                                 int32_t aHeight,
-                                 uint8_t* aImageBuffer,
-                                 int32_t aFormat,
-                                 imgIEncoder* aEncoder,
-                                 const PRUnichar* aEncoderOptions,
-                                 nsIInputStream** aStream);
-
-private:
-  // When called asynchronously, aContext is null.
-  static nsresult
-  ExtractDataInternal(const nsAString& aType,
-                      const nsAString& aOptions,
-                      uint8_t* aImageBuffer,
-                      int32_t aFormat,
-                      const nsIntSize aSize,
-                      nsICanvasRenderingContextInternal* aContext,
-                      nsIInputStream** aStream,
-                      imgIEncoder* aEncoder);
-
-  // Creates and returns an encoder instance of the type specified in aType.
-  // aType may change to "image/png" if no instance of the original type could
-  // be created and we had to fall back to a PNG encoder. A return value of
-  // NULL should be interpreted as NS_IMAGELIB_ERROR_NO_ENCODER and aType is
-  // undefined in this case.
-  static already_AddRefed<imgIEncoder> GetImageEncoder(nsAString& aType);
-
-  friend class EncodingRunnable;
-};
-
-} // namespace dom
-} // namespace mozilla
-
-#endif // ImageEncoder_h
\ No newline at end of file
--- a/content/canvas/src/Makefile.in
+++ b/content/canvas/src/Makefile.in
@@ -17,11 +17,10 @@ CXXFLAGS	+= $(MOZ_CAIRO_CFLAGS) $(MOZ_PI
 INCLUDES	+= \
 		-I$(srcdir)/../../../layout/xul/base/src \
 		-I$(srcdir)/../../../layout/style \
 		-I$(srcdir)/../../../layout/generic \
 		-I$(srcdir)/../../base/src \
 		-I$(srcdir)/../../html/content/src \
 		-I$(srcdir)/../../../js/xpconnect/src \
 		-I$(srcdir)/../../../dom/base \
-		-I$(srcdir)/../../../image/src \
 		-I$(topsrcdir)/content/xul/content/src \
 		$(NULL)
--- a/content/canvas/src/WebGLContext.cpp
+++ b/content/canvas/src/WebGLContext.cpp
@@ -22,17 +22,17 @@
 #include "nsContentUtils.h"
 #include "nsIXPConnect.h"
 #include "nsError.h"
 #include "nsIGfxInfo.h"
 #include "nsIWidget.h"
 
 #include "nsIVariant.h"
 
-#include "ImageEncoder.h"
+#include "imgIEncoder.h"
 
 #include "gfxContext.h"
 #include "gfxPattern.h"
 #include "gfxUtils.h"
 
 #include "CanvasUtils.h"
 #include "nsDisplayList.h"
 
@@ -716,89 +716,67 @@ void WebGLContext::LoseOldestWebGLContex
     } else if (numContexts > kMaxWebGLContexts) {
         GenerateWarning("Exceeded %d live WebGL contexts, losing the least recently used one.",
                         kMaxWebGLContexts);
         MOZ_ASSERT(oldestContext); // if we reach this point, this can't be null
         const_cast<WebGLContext*>(oldestContext)->LoseContext();
     }
 }
 
-void
-WebGLContext::GetImageBuffer(uint8_t** aImageBuffer, int32_t* aFormat)
-{
-    *aImageBuffer = nullptr;
-    *aFormat = 0;
-
-    nsRefPtr<gfxImageSurface> imgsurf =
-        new gfxImageSurface(gfxIntSize(mWidth, mHeight),
-                            gfxASurface::ImageFormatARGB32);
-
-    if (!imgsurf || imgsurf->CairoStatus()) {
-        return;
-    }
-
-    nsRefPtr<gfxContext> ctx = new gfxContext(imgsurf);
-    if (!ctx || ctx->HasError()) {
-        return;
-    }
-
-    // Use Render() to make sure that appropriate y-flip gets applied
-    uint32_t flags = mOptions.premultipliedAlpha ? RenderFlagPremultAlpha : 0;
-    nsresult rv = Render(ctx, gfxPattern::FILTER_NEAREST, flags);
-    if (NS_FAILED(rv)) {
-        return;
-    }
-
-    int32_t format = imgIEncoder::INPUT_FORMAT_HOSTARGB;
-    if (!mOptions.premultipliedAlpha) {
-        // We need to convert to INPUT_FORMAT_RGBA, otherwise
-        // we are automatically considered premult, and unpremult'd.
-        // Yes, it is THAT silly.
-        // Except for different lossy conversions by color,
-        // we could probably just change the label, and not change the data.
-        gfxUtils::ConvertBGRAtoRGBA(imgsurf);
-        format = imgIEncoder::INPUT_FORMAT_RGBA;
-    }
-
-    static const fallible_t fallible = fallible_t();
-    uint8_t* imageBuffer = new (fallible) uint8_t[mWidth * mHeight * 4];
-    if (!imageBuffer) {
-        return;
-    }
-    memcpy(imageBuffer, imgsurf->Data(), mWidth * mHeight * 4);
-
-    *aImageBuffer = imageBuffer;
-    *aFormat = format;
-}
-
 NS_IMETHODIMP
 WebGLContext::GetInputStream(const char* aMimeType,
                              const PRUnichar* aEncoderOptions,
                              nsIInputStream **aStream)
 {
     NS_ASSERTION(gl, "GetInputStream on invalid context?");
     if (!gl)
         return NS_ERROR_FAILURE;
 
-    uint8_t* imageBuffer = nullptr;
-    int32_t format = 0;
-    GetImageBuffer(&imageBuffer, &format);
-    if (!imageBuffer) {
+    nsRefPtr<gfxImageSurface> surf = new gfxImageSurface(gfxIntSize(mWidth, mHeight),
+                                                         gfxASurface::ImageFormatARGB32);
+    if (surf->CairoStatus() != 0)
         return NS_ERROR_FAILURE;
+
+    nsRefPtr<gfxContext> tmpcx = new gfxContext(surf);
+    // Use Render() to make sure that appropriate y-flip gets applied
+    uint32_t flags = mOptions.premultipliedAlpha ? RenderFlagPremultAlpha : 0;
+    nsresult rv = Render(tmpcx, gfxPattern::FILTER_NEAREST, flags);
+    if (NS_FAILED(rv))
+        return rv;
+
+    const char encoderPrefix[] = "@mozilla.org/image/encoder;2?type=";
+    nsAutoArrayPtr<char> conid(new char[strlen(encoderPrefix) + strlen(aMimeType) + 1]);
+
+    strcpy(conid, encoderPrefix);
+    strcat(conid, aMimeType);
+
+    nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(conid);
+    if (!encoder)
+        return NS_ERROR_FAILURE;
+
+    int format = imgIEncoder::INPUT_FORMAT_HOSTARGB;
+    if (!mOptions.premultipliedAlpha) {
+        // We need to convert to INPUT_FORMAT_RGBA, otherwise
+        // we are automatically considered premult, and unpremult'd.
+        // Yes, it is THAT silly.
+        // Except for different lossy conversions by color,
+        // we could probably just change the label, and not change the data.
+        gfxUtils::ConvertBGRAtoRGBA(surf);
+        format = imgIEncoder::INPUT_FORMAT_RGBA;
     }
 
-    nsCString enccid("@mozilla.org/image/encoder;2?type=");
-    enccid += aMimeType;
-    nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(enccid.get());
-    if (!encoder) {
-        return NS_ERROR_FAILURE;
-    }
+    rv = encoder->InitFromData(surf->Data(),
+                               mWidth * mHeight * 4,
+                               mWidth, mHeight,
+                               surf->Stride(),
+                               format,
+                               nsDependentString(aEncoderOptions));
+    NS_ENSURE_SUCCESS(rv, rv);
 
-    return ImageEncoder::GetInputStream(mWidth, mHeight, imageBuffer, format,
-                                        encoder, aEncoderOptions, aStream);
+    return CallQueryInterface(encoder, aStream);
 }
 
 NS_IMETHODIMP
 WebGLContext::GetThebesSurface(gfxASurface **surface)
 {
     return NS_ERROR_NOT_AVAILABLE;
 }
 
--- a/content/canvas/src/WebGLContext.h
+++ b/content/canvas/src/WebGLContext.h
@@ -71,17 +71,17 @@ class WebGLShaderPrecisionFormat;
 class WebGLTexture;
 class WebGLVertexArray;
 
 namespace dom {
 class ImageData;
 
 struct WebGLContextAttributes;
 struct WebGLContextAttributesInitializer;
-template<typename> class Nullable;
+template<typename> struct Nullable;
 }
 
 using WebGLTexelConversions::WebGLTexelFormat;
 
 WebGLTexelFormat GetWebGLTexelFormat(GLenum format, GLenum type);
 
 struct WebGLContextOptions {
     // these are defaults
@@ -155,17 +155,16 @@ public:
     NS_IMETHOD SetDimensions(int32_t width, int32_t height) MOZ_OVERRIDE;
     NS_IMETHOD InitializeWithSurface(nsIDocShell *docShell, gfxASurface *surface, int32_t width, int32_t height) MOZ_OVERRIDE
         { return NS_ERROR_NOT_IMPLEMENTED; }
     NS_IMETHOD Reset() MOZ_OVERRIDE
         { /* (InitializeWithSurface) */ return NS_ERROR_NOT_IMPLEMENTED; }
     NS_IMETHOD Render(gfxContext *ctx,
                       gfxPattern::GraphicsFilter f,
                       uint32_t aFlags = RenderFlagPremultAlpha) MOZ_OVERRIDE;
-    virtual void GetImageBuffer(uint8_t** aImageBuffer, int32_t* aFormat);
     NS_IMETHOD GetInputStream(const char* aMimeType,
                               const PRUnichar* aEncoderOptions,
                               nsIInputStream **aStream) MOZ_OVERRIDE;
     NS_IMETHOD GetThebesSurface(gfxASurface **surface) MOZ_OVERRIDE;
     mozilla::TemporaryRef<mozilla::gfx::SourceSurface> GetSurfaceSnapshot() MOZ_OVERRIDE
         { return nullptr; }
 
     NS_IMETHOD SetIsOpaque(bool b) MOZ_OVERRIDE { return NS_OK; };
--- a/content/canvas/src/moz.build
+++ b/content/canvas/src/moz.build
@@ -17,17 +17,16 @@ EXPORTS.mozilla.dom += [
 
 CPP_SOURCES += [
     'CanvasImageCache.cpp',
     'CanvasRenderingContext2D.cpp',
     'CanvasUtils.cpp',
     'DocumentRendererChild.cpp',
     'DocumentRendererParent.cpp',
     'ImageData.cpp',
-    'ImageEncoder.cpp',
 ]
 
 if CONFIG['MOZ_WEBGL']:
     CPP_SOURCES += [
         'WebGLActiveInfo.cpp',
         'WebGLBuffer.cpp',
         'WebGL1Context.cpp',
         'WebGL2Context.cpp',
--- a/content/canvas/test/test_mozGetAsFile.html
+++ b/content/canvas/test/test_mozGetAsFile.html
@@ -2,50 +2,49 @@
 <title>Canvas test: mozGetAsFile</title>
 <script src="/MochiKit/MochiKit.js"></script>
 <script src="/tests/SimpleTest/SimpleTest.js"></script>
 <link rel="stylesheet" href="/tests/SimpleTest/test.css">
 <body>
 <canvas id="c" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
 <script>
 
-function compareAsync(file, canvas, type, callback)
+var gCompares = 0;
+
+function compareAsync(file, canvas, type)
 {
+  ++gCompares;
+
   var reader = new FileReader();
   reader.onload = 
     function(e) {
       is(e.target.result, canvas.toDataURL(type),
  "<canvas>.mozGetAsFile().getAsDataURL() should equal <canvas>.toDataURL()");
-      callback(canvas);
+      if (--gCompares == 0) {
+        SimpleTest.finish();
+      }
     };
   reader.readAsDataURL(file);
 }
 
-function test1(canvas)
-{
-  var pngfile = canvas.mozGetAsFile("foo.png");
-  is(pngfile.type, "image/png", "Default type for mozGetAsFile should be PNG");
-  compareAsync(pngfile, canvas, "image/png", test2);
-  is(pngfile.name, "foo.png", "File name should be what we passed in");
-}
-
-function test2(canvas)
-{
-  var jpegfile = canvas.mozGetAsFile("bar.jpg", "image/jpeg");
-  is(jpegfile.type, "image/jpeg",
-     "When a valid type is specified that should be returned");
-  compareAsync(jpegfile, canvas, "image/jpeg", SimpleTest.finish);
-  is(jpegfile.name, "bar.jpg", "File name should be what we passed in");
-}
-
 SimpleTest.waitForExplicitFinish();
 addLoadEvent(function () {
 
 var canvas = document.getElementById('c');
 var ctx = canvas.getContext('2d');
+
 ctx.drawImage(document.getElementById('yellow75.png'), 0, 0);
 
-test1(canvas);
+var pngfile = canvas.mozGetAsFile("foo.png");
+is(pngfile.type, "image/png", "Default type for mozGetAsFile should be PNG");
+compareAsync(pngfile, canvas, "image/png");
+is(pngfile.name, "foo.png", "File name should be what we passed in");
+
+var jpegfile = canvas.mozGetAsFile("bar.jpg", "image/jpeg");
+is(jpegfile.type, "image/jpeg",
+   "When a valid type is specified that should be returned");
+compareAsync(jpegfile, canvas, "image/jpeg");
+is(jpegfile.name, "bar.jpg", "File name should be what we passed in");
 
 });
 </script>
 <img src="image_yellow75.png" id="yellow75.png" class="resource">
 
--- a/content/canvas/test/test_toBlob.html
+++ b/content/canvas/test/test_toBlob.html
@@ -1,48 +1,42 @@
 <!DOCTYPE HTML>
-<title>Canvas test: toBlob</title>
+<title>Canvas test: mozGetAsFile</title>
 <script src="/MochiKit/MochiKit.js"></script>
 <script src="/tests/SimpleTest/SimpleTest.js"></script>
 <link rel="stylesheet" href="/tests/SimpleTest/test.css">
 <body>
 <canvas id="c" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
 <script>
 
-function BlobListener(type, canvas, callback, file)
+var gCompares = 2;
+
+function BlobListener(type, canvas, file)
 {
   is(file.type, type,
      "When a valid type is specified that should be returned");
   var reader = new FileReader();
-  reader.onload =
+  reader.onload = 
     function(e) {
       is(e.target.result, canvas.toDataURL(type),
-  "<canvas>.mozGetAsFile().getAsDataURL() should equal <canvas>.toDataURL()");
-      callback(canvas);
+ "<canvas>.mozGetAsFile().getAsDataURL() should equal <canvas>.toDataURL()");
+      if (--gCompares == 0) {
+        SimpleTest.finish();
+      }
     };
   reader.readAsDataURL(file);
 }
 
-function test1(canvas)
-{
-  canvas.toBlob(BlobListener.bind(undefined, "image/png", canvas, test2));
-}
-
-function test2(canvas)
-{
-  canvas.toBlob(
-    BlobListener.bind(undefined, "image/jpeg", canvas, SimpleTest.finish),
-    "image/jpeg");
-}
-
 SimpleTest.waitForExplicitFinish();
 addLoadEvent(function () {
 
 var canvas = document.getElementById('c');
 var ctx = canvas.getContext('2d');
+
 ctx.drawImage(document.getElementById('yellow75.png'), 0, 0);
 
-test1(canvas);
+canvas.toBlob(BlobListener.bind(undefined, "image/png", canvas));
+canvas.toBlob(BlobListener.bind(undefined, "image/jpeg", canvas), "image/jpeg");
 
 });
 </script>
 <img src="image_yellow75.png" id="yellow75.png" class="resource">
 
--- a/content/events/public/EventTarget.h
+++ b/content/events/public/EventTarget.h
@@ -17,17 +17,17 @@ class nsIDOMEventListener;
 namespace mozilla {
 
 class ErrorResult;
 
 namespace dom {
 
 class EventListener;
 class EventHandlerNonNull;
-template <class T> class Nullable;
+template <class T> struct Nullable;
 
 // IID for the dom::EventTarget interface
 #define NS_EVENTTARGET_IID \
 { 0x0a5aed21, 0x0bab, 0x48b3, \
  { 0xbe, 0x4b, 0xd4, 0xf9, 0xd4, 0xea, 0xc7, 0xdb } }
 
 class EventTarget : public nsIDOMEventTarget,
                     public nsWrapperCache
--- a/content/html/content/public/HTMLCanvasElement.h
+++ b/content/html/content/public/HTMLCanvasElement.h
@@ -222,19 +222,20 @@ protected:
   nsIntSize GetWidthHeight();
 
   nsresult UpdateContext(JSContext* aCx, JS::Handle<JS::Value> options);
   nsresult ParseParams(JSContext* aCx,
                        const nsAString& aType,
                        const JS::Value& aEncoderOptions,
                        nsAString& aParams,
                        bool* usingCustomParseOptions);
-  nsresult ExtractData(nsAString& aType,
+  nsresult ExtractData(const nsAString& aType,
                        const nsAString& aOptions,
-                       nsIInputStream** aStream);
+                       nsIInputStream** aStream,
+                       bool& aFellBackToPNG);
   nsresult ToDataURLImpl(JSContext* aCx,
                          const nsAString& aMimeType,
                          const JS::Value& aEncoderOptions,
                          nsAString& aDataURL);
   nsresult MozGetAsFileImpl(const nsAString& aName,
                             const nsAString& aType,
                             nsIDOMFile** aResult);
   nsresult GetContextHelper(const nsAString& aContextId,
--- a/content/html/content/src/HTMLCanvasElement.cpp
+++ b/content/html/content/src/HTMLCanvasElement.cpp
@@ -1,19 +1,19 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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 "mozilla/dom/HTMLCanvasElement.h"
 
-#include "ImageEncoder.h"
+#include "Layers.h"
+#include "imgIEncoder.h"
 #include "jsapi.h"
 #include "jsfriendapi.h"
-#include "Layers.h"
 #include "mozilla/Base64.h"
 #include "mozilla/CheckedInt.h"
 #include "mozilla/dom/CanvasRenderingContext2D.h"
 #include "mozilla/dom/HTMLCanvasElementBinding.h"
 #include "mozilla/dom/UnionTypes.h"
 #include "mozilla/gfx/Rect.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Telemetry.h"
@@ -40,16 +40,38 @@ using namespace mozilla::layers;
 
 NS_IMPL_NS_NEW_HTML_ELEMENT(Canvas)
 
 namespace {
 
 typedef mozilla::dom::HTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement
 HTMLImageOrCanvasOrVideoElement;
 
+class ToBlobRunnable : public nsRunnable
+{
+public:
+  ToBlobRunnable(nsIFileCallback* aCallback,
+                 nsIDOMBlob* aBlob)
+    : mCallback(aCallback),
+      mBlob(aBlob)
+  {
+    NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  }
+
+  NS_IMETHOD Run()
+  {
+    NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+    mCallback->Receive(mBlob);
+    return NS_OK;
+  }
+private:
+  nsCOMPtr<nsIFileCallback> mCallback;
+  nsCOMPtr<nsIDOMBlob> mBlob;
+};
+
 } // anonymous namespace
 
 namespace mozilla {
 namespace dom {
 
 class HTMLCanvasPrintState : public nsIDOMMozCanvasPrintState
 {
 public:
@@ -340,20 +362,20 @@ HTMLCanvasElement::ToDataURL(const nsASt
 NS_IMETHODIMP
 HTMLCanvasElement::MozFetchAsStream(nsIInputStreamCallback *aCallback,
                                     const nsAString& aType)
 {
   if (!nsContentUtils::IsCallerChrome())
     return NS_ERROR_FAILURE;
 
   nsresult rv;
+  bool fellBackToPNG = false;
   nsCOMPtr<nsIInputStream> inputData;
 
-  nsAutoString type(aType);
-  rv = ExtractData(type, EmptyString(), getter_AddRefs(inputData));
+  rv = ExtractData(aType, EmptyString(), getter_AddRefs(inputData), fellBackToPNG);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIAsyncInputStream> asyncData = do_QueryInterface(inputData, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIThread> mainThread;
   rv = NS_GetMainThread(getter_AddRefs(mainThread));
   NS_ENSURE_SUCCESS(rv, rv);
@@ -383,25 +405,78 @@ HTMLCanvasElement::GetMozPrintCallback()
 NS_IMETHODIMP
 HTMLCanvasElement::GetMozPrintCallback(nsIPrintCallback** aCallback)
 {
   NS_IF_ADDREF(*aCallback = GetMozPrintCallback());
   return NS_OK;
 }
 
 nsresult
-HTMLCanvasElement::ExtractData(nsAString& aType,
+HTMLCanvasElement::ExtractData(const nsAString& aType,
                                const nsAString& aOptions,
-                               nsIInputStream** aStream)
+                               nsIInputStream** aStream,
+                               bool& aFellBackToPNG)
 {
-  return ImageEncoder::ExtractData(aType,
-                                   aOptions,
-                                   GetSize(),
-                                   mCurrentContext,
-                                   aStream);
+  // note that if we don't have a current context, the spec says we're
+  // supposed to just return transparent black pixels of the canvas
+  // dimensions.
+  nsRefPtr<gfxImageSurface> emptyCanvas;
+  nsIntSize size = GetWidthHeight();
+  if (!mCurrentContext) {
+    emptyCanvas = new gfxImageSurface(gfxIntSize(size.width, size.height), gfxASurface::ImageFormatARGB32);
+    if (emptyCanvas->CairoStatus()) {
+      return NS_ERROR_INVALID_ARG;
+    }
+  }
+
+  nsresult rv;
+
+  // get image bytes
+  nsCOMPtr<nsIInputStream> imgStream;
+  NS_ConvertUTF16toUTF8 encoderType(aType);
+
+ try_again:
+  if (mCurrentContext) {
+    rv = mCurrentContext->GetInputStream(encoderType.get(),
+                                         nsPromiseFlatString(aOptions).get(),
+                                         getter_AddRefs(imgStream));
+  } else {
+    // no context, so we have to encode the empty image we created above
+    nsCString enccid("@mozilla.org/image/encoder;2?type=");
+    enccid += encoderType;
+
+    nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(enccid.get(), &rv);
+    if (NS_SUCCEEDED(rv) && encoder) {
+      rv = encoder->InitFromData(emptyCanvas->Data(),
+                                 size.width * size.height * 4,
+                                 size.width,
+                                 size.height,
+                                 size.width * 4,
+                                 imgIEncoder::INPUT_FORMAT_HOSTARGB,
+                                 aOptions);
+      if (NS_SUCCEEDED(rv)) {
+        imgStream = do_QueryInterface(encoder);
+      }
+    } else {
+      rv = NS_ERROR_FAILURE;
+    }
+  }
+
+  if (NS_FAILED(rv) && !aFellBackToPNG) {
+    // Try image/png instead.
+    // XXX ERRMSG we need to report an error to developers here! (bug 329026)
+    aFellBackToPNG = true;
+    encoderType.AssignLiteral("image/png");
+    goto try_again;
+  }
+
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  imgStream.forget(aStream);
+  return NS_OK;
 }
 
 nsresult
 HTMLCanvasElement::ParseParams(JSContext* aCx,
                                const nsAString& aType,
                                const JS::Value& aEncoderOptions,
                                nsAString& aParams,
                                bool* usingCustomParseOptions)
@@ -442,16 +517,18 @@ HTMLCanvasElement::ParseParams(JSContext
 }
 
 nsresult
 HTMLCanvasElement::ToDataURLImpl(JSContext* aCx,
                                  const nsAString& aMimeType,
                                  const JS::Value& aEncoderOptions,
                                  nsAString& aDataURL)
 {
+  bool fallbackToPNG = false;
+
   nsIntSize size = GetWidthHeight();
   if (size.height == 0 || size.width == 0) {
     aDataURL = NS_LITERAL_STRING("data:,");
     return NS_OK;
   }
 
   nsAutoString type;
   nsresult rv = nsContentUtils::ASCIIToLower(aMimeType, type);
@@ -462,37 +539,43 @@ HTMLCanvasElement::ToDataURLImpl(JSConte
   nsAutoString params;
   bool usingCustomParseOptions;
   rv = ParseParams(aCx, type, aEncoderOptions, params, &usingCustomParseOptions);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   nsCOMPtr<nsIInputStream> stream;
-  rv = ExtractData(type, params, getter_AddRefs(stream));
+  rv = ExtractData(type, params, getter_AddRefs(stream), fallbackToPNG);
 
   // If there are unrecognized custom parse options, we should fall back to
   // the default values for the encoder without any options at all.
   if (rv == NS_ERROR_INVALID_ARG && usingCustomParseOptions) {
-    rv = ExtractData(type, EmptyString(), getter_AddRefs(stream));
+    fallbackToPNG = false;
+    rv = ExtractData(type, EmptyString(), getter_AddRefs(stream), fallbackToPNG);
   }
 
   NS_ENSURE_SUCCESS(rv, rv);
 
   // build data URL string
-  aDataURL = NS_LITERAL_STRING("data:") + type + NS_LITERAL_STRING(";base64,");
+  if (fallbackToPNG)
+    aDataURL = NS_LITERAL_STRING("data:image/png;base64,");
+  else
+    aDataURL = NS_LITERAL_STRING("data:") + type +
+      NS_LITERAL_STRING(";base64,");
 
   uint64_t count;
   rv = stream->Available(&count);
   NS_ENSURE_SUCCESS(rv, rv);
   NS_ENSURE_TRUE(count <= UINT32_MAX, NS_ERROR_FILE_TOO_BIG);
 
   return Base64EncodeInputStream(stream, aDataURL, (uint32_t)count, aDataURL.Length());
 }
 
+// XXXkhuey the encoding should be off the main thread, but we're lazy.
 NS_IMETHODIMP
 HTMLCanvasElement::ToBlob(nsIFileCallback* aCallback,
                           const nsAString& aType,
                           const JS::Value& aEncoderOptions,
                           JSContext* aCx)
 {
   // do a trust check if this is a write-only canvas
   if (mWriteOnly && !nsContentUtils::IsCallerChrome()) {
@@ -511,34 +594,53 @@ HTMLCanvasElement::ToBlob(nsIFileCallbac
 
   nsAutoString params;
   bool usingCustomParseOptions;
   rv = ParseParams(aCx, type, aEncoderOptions, params, &usingCustomParseOptions);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
-  JSContext* cx = nsContentUtils::GetCurrentJSContext();
-  nsCOMPtr<nsIThread> currentThread = NS_GetCurrentThread();
+  bool fallbackToPNG = false;
 
-  uint8_t* imageBuffer = nullptr;
-  int32_t format = 0;
-  if (mCurrentContext) {
-    mCurrentContext->GetImageBuffer(&imageBuffer, &format);
+  nsCOMPtr<nsIInputStream> stream;
+  rv = ExtractData(type, params, getter_AddRefs(stream), fallbackToPNG);
+  // If there are unrecognized custom parse options, we should fall back to
+  // the default values for the encoder without any options at all.
+  if (rv == NS_ERROR_INVALID_ARG && usingCustomParseOptions) {
+    fallbackToPNG = false;
+    rv = ExtractData(type, EmptyString(), getter_AddRefs(stream), fallbackToPNG);
+  }
+
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (fallbackToPNG) {
+    type.AssignLiteral("image/png");
   }
 
-  return ImageEncoder::ExtractDataAsync(type,
-                                        params,
-                                        usingCustomParseOptions,
-                                        imageBuffer,
-                                        format,
-                                        GetSize(),
-                                        mCurrentContext,
-                                        cx,
-                                        aCallback);
+  uint64_t imgSize;
+  rv = stream->Available(&imgSize);
+  NS_ENSURE_SUCCESS(rv, rv);
+  NS_ENSURE_TRUE(imgSize <= UINT32_MAX, NS_ERROR_FILE_TOO_BIG);
+
+  void* imgData = nullptr;
+  rv = NS_ReadInputStreamToBuffer(stream, &imgData, imgSize);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // The DOMFile takes ownership of the buffer
+  nsRefPtr<nsDOMMemoryFile> blob =
+    new nsDOMMemoryFile(imgData, imgSize, type);
+
+  JSContext* cx = nsContentUtils::GetCurrentJSContext();
+  if (cx) {
+    JS_updateMallocCounter(cx, imgSize);
+  }
+
+  nsRefPtr<ToBlobRunnable> runnable = new ToBlobRunnable(aCallback, blob);
+  return NS_DispatchToCurrentThread(runnable);
 }
 
 already_AddRefed<nsIDOMFile>
 HTMLCanvasElement::MozGetAsFile(const nsAString& aName,
                                 const nsAString& aType,
                                 ErrorResult& aRv)
 {
   nsCOMPtr<nsIDOMFile> file;
@@ -562,20 +664,27 @@ HTMLCanvasElement::MozGetAsFile(const ns
   return MozGetAsFileImpl(aName, aType, aResult);
 }
 
 nsresult
 HTMLCanvasElement::MozGetAsFileImpl(const nsAString& aName,
                                     const nsAString& aType,
                                     nsIDOMFile** aResult)
 {
+  bool fallbackToPNG = false;
+
   nsCOMPtr<nsIInputStream> stream;
+  nsresult rv = ExtractData(aType, EmptyString(), getter_AddRefs(stream),
+                            fallbackToPNG);
+  NS_ENSURE_SUCCESS(rv, rv);
+
   nsAutoString type(aType);
-  nsresult rv = ExtractData(type, EmptyString(), getter_AddRefs(stream));
-  NS_ENSURE_SUCCESS(rv, rv);
+  if (fallbackToPNG) {
+    type.AssignLiteral("image/png");
+  }
 
   uint64_t imgSize;
   rv = stream->Available(&imgSize);
   NS_ENSURE_SUCCESS(rv, rv);
   NS_ENSURE_TRUE(imgSize <= UINT32_MAX, NS_ERROR_FILE_TOO_BIG);
 
   void* imgData = nullptr;
   rv = NS_ReadInputStreamToBuffer(stream, &imgData, (uint32_t)imgSize);
--- a/content/html/content/src/HTMLPropertiesCollection.cpp
+++ b/content/html/content/src/HTMLPropertiesCollection.cpp
@@ -8,16 +8,17 @@
 #include "nsIDocument.h"
 #include "nsContentUtils.h"
 #include "nsGenericHTMLElement.h"
 #include "nsVariant.h"
 #include "nsDOMSettableTokenList.h"
 #include "nsAttrValue.h"
 #include "nsWrapperCacheInlines.h"
 #include "mozilla/dom/HTMLPropertiesCollectionBinding.h"
+#include "jsapi.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLPropertiesCollection)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(HTMLPropertiesCollection)
   // SetDocument(nullptr) ensures that we remove ourselves as a mutation observer
--- a/content/html/content/src/Makefile.in
+++ b/content/html/content/src/Makefile.in
@@ -17,13 +17,12 @@ INCLUDES	+= \
 		-I$(srcdir)/../../../../layout/tables \
 		-I$(srcdir)/../../../../layout/xul/base/src \
 		-I$(srcdir)/../../../../layout/generic \
 		-I$(srcdir)/../../../../dom/base \
 		-I$(srcdir)/../../../../editor/libeditor/base \
 		-I$(srcdir)/../../../../editor/libeditor/text \
 		-I$(srcdir)/../../../../editor/txmgr/src \
 		-I$(srcdir)/../../../../netwerk/base/src \
-		-I$(srcdir)/../../../../content/canvas/src \
 		-I$(srcdir) \
 		-I$(topsrcdir)/xpcom/ds \
 		-I$(topsrcdir)/content/media/ \
 		$(NULL)
--- a/content/xul/document/src/nsXULPrototypeCache.cpp
+++ b/content/xul/document/src/nsXULPrototypeCache.cpp
@@ -18,16 +18,17 @@
 #include "nsIObserverService.h"
 #include "nsIStringStream.h"
 #include "nsIStorageStream.h"
 
 #include "nsNetUtil.h"
 #include "nsAppDirectoryServiceDefs.h"
 
 #include "jsapi.h"
+#include "js/Tracer.h"
 
 #include "mozilla/Preferences.h"
 #include "mozilla/scache/StartupCache.h"
 #include "mozilla/scache/StartupCacheUtils.h"
 #include "mozilla/Telemetry.h"
 
 using namespace mozilla;
 using namespace mozilla::scache;
--- a/dom/base/nsWrapperCacheInlines.h
+++ b/dom/base/nsWrapperCacheInlines.h
@@ -3,17 +3,17 @@
  * 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 nsWrapperCacheInline_h___
 #define nsWrapperCacheInline_h___
 
 #include "nsWrapperCache.h"
 #include "js/GCAPI.h"
-#include "jsapi.h"
+#include "js/Tracer.h"
 
 inline JSObject*
 nsWrapperCache::GetWrapper() const
 {
     JSObject* obj = GetWrapperPreserveColor();
     if (obj) {
       JS::ExposeObjectToActiveJS(obj);
     }
--- a/dom/bindings/TypedArray.h
+++ b/dom/bindings/TypedArray.h
@@ -5,16 +5,17 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_TypedArray_h
 #define mozilla_dom_TypedArray_h
 
 #include "jsapi.h"
 #include "jsfriendapi.h"
 #include "js/RootingAPI.h"
+#include "js/Tracer.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/BindingDeclarations.h"
 #include "mozilla/Util.h" // for Maybe
 #include "nsWrapperCache.h"
 
 namespace mozilla {
 namespace dom {
 
--- a/dom/mobilemessage/interfaces/nsIMobileMessageCallback.idl
+++ b/dom/mobilemessage/interfaces/nsIMobileMessageCallback.idl
@@ -9,32 +9,33 @@ dictionary SmsThreadListItem
 {
   unsigned long long id;
   DOMString senderOrReceiver;
   unsigned long long timestamp;
   DOMString body;
   unsigned long long unreadCount;
 };
 
-[scriptable, uuid(399125a8-00d2-11e3-8d12-3fba4465c097)]
+[scriptable, uuid(5f82f826-1956-11e3-a2bb-9b043b33de27)]
 interface nsIMobileMessageCallback : nsISupports
 {
   /**
    * All SMS related errors.
    * Make sure to keep this list in sync with the list in:
    * embedding/android/GeckoSmsManager.java
    */
   const unsigned short SUCCESS_NO_ERROR          = 0;
   const unsigned short NO_SIGNAL_ERROR           = 1;
   const unsigned short NOT_FOUND_ERROR           = 2;
   const unsigned short UNKNOWN_ERROR             = 3;
   const unsigned short INTERNAL_ERROR            = 4;
   const unsigned short NO_SIM_CARD_ERROR         = 5;
   const unsigned short RADIO_DISABLED_ERROR      = 6;
   const unsigned short INVALID_ADDRESS_ERROR     = 7;
+  const unsigned short FDN_CHECK_ERROR           = 8;
 
   /**
    * |message| can be nsIDOMMoz{Mms,Sms}Message.
    */
   void notifyMessageSent(in nsISupports message);
   void notifySendMessageFailed(in long error);
 
   /**
--- a/dom/mobilemessage/src/MobileMessageCallback.cpp
+++ b/dom/mobilemessage/src/MobileMessageCallback.cpp
@@ -99,16 +99,19 @@ MobileMessageCallback::NotifyError(int32
       errorStr = NS_LITERAL_STRING("NoSimCardError");
       break;
     case nsIMobileMessageCallback::RADIO_DISABLED_ERROR:
       errorStr = NS_LITERAL_STRING("RadioDisabledError");
       break;
     case nsIMobileMessageCallback::INVALID_ADDRESS_ERROR:
       errorStr = NS_LITERAL_STRING("InvalidAddressError");
       break;
+    case nsIMobileMessageCallback::FDN_CHECK_ERROR:
+      errorStr = NS_LITERAL_STRING("FdnCheckError");
+      break;
     default: // SUCCESS_NO_ERROR is handled above.
       MOZ_CRASH("Should never get here!");
   }
 
   if (aAsync) {
     nsCOMPtr<nsIDOMRequestService> rs =
       do_GetService(DOMREQUEST_SERVICE_CONTRACTID);
     NS_ENSURE_TRUE(rs, NS_ERROR_FAILURE);
--- a/dom/src/geolocation/nsGeolocation.cpp
+++ b/dom/src/geolocation/nsGeolocation.cpp
@@ -1006,36 +1006,21 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMGeoGeolocation)
   NS_INTERFACE_MAP_ENTRY(nsIDOMGeoGeolocation)
   NS_INTERFACE_MAP_ENTRY(nsIGeolocationUpdate)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(Geolocation)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(Geolocation)
 
-NS_IMPL_CYCLE_COLLECTION_CLASS(Geolocation)
-
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Geolocation)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mCachedPosition)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
-  tmp->mPendingRequests.Clear();
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mPendingCallbacks)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mWatchingCallbacks)
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
-
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Geolocation)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCachedPosition)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
-  for (uint32_t i = 0; i < tmp->mPendingRequests.Length(); ++i)
-    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingRequests[i].request)
-
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingCallbacks)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWatchingCallbacks)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
-NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(Geolocation)
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_4(Geolocation,
+                                        mCachedPosition,
+                                        mPendingCallbacks,
+                                        mWatchingCallbacks,
+                                        mPendingRequests)
 
 Geolocation::Geolocation()
 : mLastWatchId(0)
 {
   SetIsDOMBinding();
 }
 
 Geolocation::~Geolocation()
@@ -1243,18 +1228,17 @@ Geolocation::GetCurrentPosition(GeoPosit
     return NS_OK;
   }
 
   if (!mOwner && !nsContentUtils::IsCallerChrome()) {
     return NS_ERROR_FAILURE;
   }
 
   if (sGeoInitPending) {
-    PendingRequest req = { request, PendingRequest::GetCurrentPosition };
-    mPendingRequests.AppendElement(req);
+    mPendingRequests.AppendElement(request);
     return NS_OK;
   }
 
   return GetCurrentPositionReady(request);
 }
 
 nsresult
 Geolocation::GetCurrentPositionReady(nsGeolocationRequest* aRequest)
@@ -1337,18 +1321,17 @@ Geolocation::WatchPosition(GeoPositionCa
     return NS_OK;
   }
 
   if (!mOwner && !nsContentUtils::IsCallerChrome()) {
     return NS_ERROR_FAILURE;
   }
 
   if (sGeoInitPending) {
-    PendingRequest req = { request, PendingRequest::WatchPosition };
-    mPendingRequests.AppendElement(req);
+    mPendingRequests.AppendElement(request);
     return NS_OK;
   }
 
   return WatchPositionReady(request);
 }
 
 nsresult
 Geolocation::WatchPositionReady(nsGeolocationRequest* aRequest)
@@ -1382,39 +1365,35 @@ Geolocation::ClearWatch(int32_t aWatchId
       RemoveRequest(mWatchingCallbacks[i]);
       break;
     }
   }
 
   // make sure we also search through the pending requests lists for
   // watches to clear...
   for (uint32_t i = 0, length = mPendingRequests.Length(); i < length; ++i) {
-    if ((mPendingRequests[i].type == PendingRequest::WatchPosition) &&
-        (mPendingRequests[i].request->WatchId() == aWatchId)) {
-      mPendingRequests[i].request->Shutdown();
+    if (mPendingRequests[i]->IsWatch() &&
+        (mPendingRequests[i]->WatchId() == aWatchId)) {
+      mPendingRequests[i]->Shutdown();
       mPendingRequests.RemoveElementAt(i);
       break;
     }
   }
 
   return NS_OK;
 }
 
 void
 Geolocation::ServiceReady()
 {
   for (uint32_t length = mPendingRequests.Length(); length > 0; --length) {
-    switch (mPendingRequests[0].type) {
-      case PendingRequest::GetCurrentPosition:
-        GetCurrentPositionReady(mPendingRequests[0].request);
-        break;
-
-      case PendingRequest::WatchPosition:
-        WatchPositionReady(mPendingRequests[0].request);
-        break;
+    if (mPendingRequests[0]->IsWatch()) {
+      WatchPositionReady(mPendingRequests[0]);
+    } else {
+      GetCurrentPositionReady(mPendingRequests[0]);
     }
 
     mPendingRequests.RemoveElementAt(0);
   }
 }
 
 bool
 Geolocation::WindowOwnerStillExists()
--- a/dom/src/geolocation/nsGeolocation.h
+++ b/dom/src/geolocation/nsGeolocation.h
@@ -204,28 +204,18 @@ private:
   nsRefPtr<nsGeolocationService> mService;
 
   // cached Position wrapper
   nsRefPtr<Position> mCachedPosition;
 
   // Watch ID
   uint32_t mLastWatchId;
 
-  // Pending requests are used when the service is not ready:
-  class PendingRequest
-  {
-  public:
-    nsRefPtr<nsGeolocationRequest> request;
-    enum {
-      GetCurrentPosition,
-      WatchPosition
-    } type;
-  };
-
-  nsTArray<PendingRequest> mPendingRequests;
+  // Pending requests are used when the service is not ready
+  nsTArray<nsRefPtr<nsGeolocationRequest> > mPendingRequests;
 };
 
 class PositionError MOZ_FINAL : public nsIDOMGeoPositionError,
                                 public nsWrapperCache
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(PositionError)
--- a/dom/system/gonk/RadioInterfaceLayer.js
+++ b/dom/system/gonk/RadioInterfaceLayer.js
@@ -2671,16 +2671,19 @@ RadioInterface.prototype = {
                                 (function(context, response) {
         if (response.errorMsg) {
           // Failed to send SMS out.
           let error = Ci.nsIMobileMessageCallback.UNKNOWN_ERROR;
           switch (response.errorMsg) {
             case RIL.ERROR_RADIO_NOT_AVAILABLE:
               error = Ci.nsIMobileMessageCallback.NO_SIGNAL_ERROR;
               break;
+            case RIL.ERROR_FDN_CHECK_FAILURE:
+              error = Ci.nsIMobileMessageCallback.FDN_CHECK_ERROR;
+              break;
           }
 
           if (context.silent) {
             context.request.notifySendMessageFailed(error);
             return false;
           }
 
           gMobileMessageDatabaseService
--- a/dom/workers/EventListenerManager.cpp
+++ b/dom/workers/EventListenerManager.cpp
@@ -2,18 +2,19 @@
 /* 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 "EventListenerManager.h"
 
 #include "jsapi.h"
 #include "jsfriendapi.h"
+#include "js/GCAPI.h"
+#include "js/Tracer.h"
 #include "js/Vector.h"
-#include "js/GCAPI.h"
 #include "mozilla/Util.h"
 #include "nsAutoJSValHolder.h"
 
 #include "Events.h"
 #include "EventTarget.h"
 
 using namespace mozilla::dom;
 using namespace mozilla;
--- a/embedding/android/GeckoSmsManager.java
+++ b/embedding/android/GeckoSmsManager.java
@@ -304,16 +304,17 @@ public class GeckoSmsManager
   public final static int kNoError                = 0;
   public final static int kNoSignalError          = 1;
   public final static int kNotFoundError          = 2;
   public final static int kUnknownError           = 3;
   public final static int kInternalError          = 4;
   public final static int kNoSimCardError         = 5;
   public final static int kRadioDisabledError     = 6;
   public final static int kInvalidAddressError    = 7;
+  public final static int kFdnCheckError          = 8;
 
   private final static int kMaxMessageSize    = 160;
 
   private final static Uri kSmsContentUri     = Uri.parse("content://sms");
   private final static Uri kSmsSentContentUri = Uri.parse("content://sms/sent");
 
   private final static int kSmsTypeInbox      = 1;
   private final static int kSmsTypeSentbox    = 2;
--- a/image/test/mochitest/test_animSVGImage.html
+++ b/image/test/mochitest/test_animSVGImage.html
@@ -49,56 +49,64 @@ function takeReferenceSnapshot() {
 
   // Re-hide reference div, and take another snapshot to be sure it's gone
   referenceDiv.style.display = "none";
   let blankSnapshot2 = snapshotWindow(window, false);
   ok(compareSnapshots(blankSnapshot, blankSnapshot2, true)[0],
      "reference div should disappear when it becomes display:none");
 }
 
-function myOnStopFrame() {
+function myOnStopFrame(aRequest) {
   gOnStopFrameCounter++;
   ok(true, "myOnStopFrame called");
   let currentSnapshot = snapshotWindow(window, false);
   if (compareSnapshots(currentSnapshot, gReferenceSnapshot, true)[0]) {
     // SUCCESS!
     ok(true, "Animated image looks correct, " +
              "at call #" + gOnStopFrameCounter + " to onStopFrame");
     cleanUpAndFinish();
   }
-  else
-    setTimeout(myOnStopFrame, 1);
+  setTimeout(function() { myOnStopFrame(0, 0); }, 1000);
 }
 
 function failTest() {
   ok(false, "timing out after " + FAILURE_TIMEOUT + "ms.  " +
             "Animated image still doesn't look correct, " +
             "after call #" + gOnStopFrameCounter + " to onStopFrame");
   cleanUpAndFinish();
 }
 
 function cleanUpAndFinish() {
   // On the off chance that failTest and myOnStopFrame are triggered
   // back-to-back, use a flag to prevent multiple calls to SimpleTest.finish.
   if (gIsTestFinished) {
     return;
   }
+  let imgLoadingContent = gImg.QueryInterface(Ci.nsIImageLoadingContent);
+  imgLoadingContent.removeObserver(gMyDecoderObserver);
   SimpleTest.finish();
   gIsTestFinished = true;
 }
 
 function main() {
   takeReferenceSnapshot();
 
+  // Create, customize & attach decoder observer
+  observer = new ImageDecoderObserverStub();
+  observer.frameComplete = myOnStopFrame;
+  gMyDecoderObserver =
+    Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools)
+      .createScriptedObserver(observer);
+  let imgLoadingContent = gImg.QueryInterface(Ci.nsIImageLoadingContent);
+  imgLoadingContent.addObserver(gMyDecoderObserver);
+
   // We want to test the cold loading behavior, so clear cache in case an
   // earlier test got our image in there already.
   clearImageCache();
 
-  setTimeout(myOnStopFrame, 1);
-
   // kick off image-loading! myOnStopFrame handles the rest.
   gImg.setAttribute("src", "lime-anim-100x100.svg");
 
   // In case something goes wrong, fail earlier than mochitest timeout,
   // and with more information.
   setTimeout(failTest, FAILURE_TIMEOUT);
 }
 
--- a/js/public/Class.h
+++ b/js/public/Class.h
@@ -287,21 +287,16 @@ typedef bool
 // structures. The only exception for this rule is the case when the embedding
 // needs a tight integration with GC. In that case the embedding can check if
 // the traversal is a part of the marking phase through calling
 // JS_IsGCMarkingTracer and apply a special code like emptying caches or
 // marking its native structures.
 typedef void
 (* JSTraceOp)(JSTracer *trc, JSObject *obj);
 
-// Callback that JSTraceOp implementation can provide to return a string
-// describing the reference traced with JS_CallTracer.
-typedef void
-(* JSTraceNamePrinter)(JSTracer *trc, char *buf, size_t bufsize);
-
 // A generic type for functions mapping an object to another object, or null
 // if an error or exception was thrown on cx.
 typedef JSObject *
 (* JSObjectOp)(JSContext *cx, JS::Handle<JSObject*> obj);
 
 // Hook that creates an iterator object for a given object. Returns the
 // iterator object or null if an error or exception was thrown on cx.
 typedef JSObject *
--- a/js/public/HeapAPI.h
+++ b/js/public/HeapAPI.h
@@ -8,16 +8,22 @@
 #define js_HeapAPI_h
 
 #include "jspubtd.h"
 
 #include "js/Utility.h"
 
 /* These values are private to the JS engine. */
 namespace js {
+
+// Whether the current thread is permitted access to any part of the specified
+// runtime or zone.
+extern bool CurrentThreadCanAccessRuntime(JSRuntime *rt);
+extern bool CurrentThreadCanAccessZone(JS::Zone *zone);
+
 namespace gc {
 
 const size_t ArenaShift = 12;
 const size_t ArenaSize = size_t(1) << ArenaShift;
 const size_t ArenaMask = ArenaSize - 1;
 
 const size_t ChunkShift = 20;
 const size_t ChunkSize = size_t(1) << ChunkShift;
@@ -52,19 +58,53 @@ namespace shadow {
 
 struct ArenaHeader
 {
     JS::Zone *zone;
 };
 
 struct Zone
 {
+  protected:
+    JSRuntime *const runtime_;
+    JSTracer *const barrierTracer_;     // A pointer to the JSRuntime's |gcMarker|.
+
+  public:
     bool needsBarrier_;
 
-    Zone() : needsBarrier_(false) {}
+    Zone(JSRuntime *runtime, JSTracer *barrierTracerArg)
+      : runtime_(runtime),
+        barrierTracer_(barrierTracerArg),
+        needsBarrier_(false)
+    {}
+
+    bool needsBarrier() const {
+        return needsBarrier_;
+    }
+
+    JSTracer *barrierTracer() {
+        JS_ASSERT(needsBarrier_);
+        JS_ASSERT(js::CurrentThreadCanAccessRuntime(runtime_));
+        return barrierTracer_;
+    }
+
+    JSRuntime *runtimeFromMainThread() const {
+        JS_ASSERT(js::CurrentThreadCanAccessRuntime(runtime_));
+        return runtime_;
+    }
+
+    // Note: Unrestricted access to the zone's runtime from an arbitrary
+    // thread can easily lead to races. Use this method very carefully.
+    JSRuntime *runtimeFromAnyThread() const {
+        return runtime_;
+    }
+
+    static JS::shadow::Zone *asShadowZone(JS::Zone *zone) {
+        return reinterpret_cast<JS::shadow::Zone*>(zone);
+    }
 };
 
 } /* namespace shadow */
 } /* namespace JS */
 
 namespace js {
 namespace gc {
 
new file mode 100644
--- /dev/null
+++ b/js/public/Tracer.h
@@ -0,0 +1,191 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * 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 js_Tracer_h
+#define js_Tracer_h
+
+#include "jspubtd.h"
+
+struct JSTracer;
+
+namespace JS {
+template <typename T> class Heap;
+template <typename T> class TenuredHeap;
+}
+
+// Tracer callback, called for each traceable thing directly referenced by a
+// particular object or runtime structure. It is the callback responsibility
+// to ensure the traversal of the full object graph via calling eventually
+// JS_TraceChildren on the passed thing. In this case the callback must be
+// prepared to deal with cycles in the traversal graph.
+//
+// kind argument is one of JSTRACE_OBJECT, JSTRACE_STRING or a tag denoting
+// internal implementation-specific traversal kind. In the latter case the only
+// operations on thing that the callback can do is to call JS_TraceChildren or
+// JS_GetTraceThingInfo.
+//
+// If eagerlyTraceWeakMaps is true, when we trace a WeakMap visit all
+// of its mappings. This should be used in cases where the tracer
+// wants to use the existing liveness of entries.
+typedef void
+(* JSTraceCallback)(JSTracer *trc, void **thingp, JSGCTraceKind kind);
+
+// Callback that JSTraceOp implementation can provide to return a string
+// describing the reference traced with JS_CallTracer.
+typedef void
+(* JSTraceNamePrinter)(JSTracer *trc, char *buf, size_t bufsize);
+
+enum WeakMapTraceKind {
+    DoNotTraceWeakMaps = 0,
+    TraceWeakMapValues = 1,
+    TraceWeakMapKeysValues = 2
+};
+
+struct JSTracer {
+    JSRuntime           *runtime;
+    JSTraceCallback     callback;
+    JSTraceNamePrinter  debugPrinter;
+    const void          *debugPrintArg;
+    size_t              debugPrintIndex;
+    WeakMapTraceKind    eagerlyTraceWeakMaps;
+#ifdef JS_GC_ZEAL
+    void                *realLocation;
+#endif
+};
+
+// Set debugging information about a reference to a traceable thing to prepare
+// for the following call to JS_CallTracer.
+//
+// When printer is null, arg must be const char * or char * C string naming
+// the reference and index must be either (size_t)-1 indicating that the name
+// alone describes the reference or it must be an index into some array vector
+// that stores the reference.
+//
+// When printer callback is not null, the arg and index arguments are
+// available to the callback as debugPrintArg and debugPrintIndex fields
+// of JSTracer.
+//
+// The storage for name or callback's arguments needs to live only until
+// the following call to JS_CallTracer returns.
+//
+# define JS_SET_TRACING_DETAILS(trc, printer, arg, index)                     \
+    JS_BEGIN_MACRO                                                            \
+        (trc)->debugPrinter = (printer);                                      \
+        (trc)->debugPrintArg = (arg);                                         \
+        (trc)->debugPrintIndex = (index);                                     \
+    JS_END_MACRO
+
+// Sets the real location for a marked reference, when passing the address
+// directly is not feasable.
+//
+// FIXME: This is currently overcomplicated by our need to nest calls for Values
+// stored as keys in hash tables, but will get simplified once we can rekey
+// in-place.
+//
+#ifdef JS_GC_ZEAL
+# define JS_SET_TRACING_LOCATION(trc, location)                               \
+    JS_BEGIN_MACRO                                                            \
+        if (!(trc)->realLocation || !(location))                              \
+            (trc)->realLocation = (location);                                 \
+    JS_END_MACRO
+# define JS_UNSET_TRACING_LOCATION(trc)                                       \
+    JS_BEGIN_MACRO                                                            \
+        (trc)->realLocation = NULL;                                           \
+    JS_END_MACRO
+#else
+# define JS_SET_TRACING_LOCATION(trc, location)                               \
+    JS_BEGIN_MACRO                                                            \
+    JS_END_MACRO
+# define JS_UNSET_TRACING_LOCATION(trc)                                       \
+    JS_BEGIN_MACRO                                                            \
+    JS_END_MACRO
+#endif
+
+// Convenience macro to describe the argument of JS_CallTracer using C string
+// and index.
+# define JS_SET_TRACING_INDEX(trc, name, index)                               \
+    JS_SET_TRACING_DETAILS(trc, NULL, name, index)
+
+// Convenience macro to describe the argument of JS_CallTracer using C string.
+# define JS_SET_TRACING_NAME(trc, name)                                       \
+    JS_SET_TRACING_DETAILS(trc, NULL, name, (size_t)-1)
+
+// The JS_Call*Tracer family of functions traces the given GC thing reference.
+// This performs the tracing action configured on the given JSTracer:
+// typically calling the JSTracer::callback or marking the thing as live.
+//
+// The argument to JS_Call*Tracer is an in-out param: when the function
+// returns, the garbage collector might have moved the GC thing. In this case,
+// the reference passed to JS_Call*Tracer will be updated to the object's new
+// location. Callers of this method are responsible for updating any state
+// that is dependent on the object's address. For example, if the object's
+// address is used as a key in a hashtable, then the object must be removed
+// and re-inserted with the correct hash.
+//
+extern JS_PUBLIC_API(void)
+JS_CallValueTracer(JSTracer *trc, JS::Value *valuep, const char *name);
+
+extern JS_PUBLIC_API(void)
+JS_CallIdTracer(JSTracer *trc, jsid *idp, const char *name);
+
+extern JS_PUBLIC_API(void)
+JS_CallObjectTracer(JSTracer *trc, JSObject **objp, const char *name);
+
+extern JS_PUBLIC_API(void)
+JS_CallStringTracer(JSTracer *trc, JSString **strp, const char *name);
+
+extern JS_PUBLIC_API(void)
+JS_CallScriptTracer(JSTracer *trc, JSScript **scriptp, const char *name);
+
+extern JS_PUBLIC_API(void)
+JS_CallHeapValueTracer(JSTracer *trc, JS::Heap<JS::Value> *valuep, const char *name);
+
+extern JS_PUBLIC_API(void)
+JS_CallHeapIdTracer(JSTracer *trc, JS::Heap<jsid> *idp, const char *name);
+
+extern JS_PUBLIC_API(void)
+JS_CallHeapObjectTracer(JSTracer *trc, JS::Heap<JSObject *> *objp, const char *name);
+
+extern JS_PUBLIC_API(void)
+JS_CallHeapStringTracer(JSTracer *trc, JS::Heap<JSString *> *strp, const char *name);
+
+extern JS_PUBLIC_API(void)
+JS_CallHeapScriptTracer(JSTracer *trc, JS::Heap<JSScript *> *scriptp, const char *name);
+
+template <typename HashSetEnum>
+inline void
+JS_CallHashSetObjectTracer(JSTracer *trc, HashSetEnum &e, JSObject *const &key, const char *name)
+{
+    JSObject *updated = key;
+    JS_SET_TRACING_LOCATION(trc, reinterpret_cast<void *>(&const_cast<JSObject *&>(key)));
+    JS_CallObjectTracer(trc, &updated, name);
+    if (updated != key)
+        e.rekeyFront(key, updated);
+}
+
+// Trace an object that is known to always be tenured.  No post barriers are
+// required in this case.
+extern JS_PUBLIC_API(void)
+JS_CallTenuredObjectTracer(JSTracer *trc, JS::TenuredHeap<JSObject *> *objp, const char *name);
+
+// API for JSTraceCallback implementations.
+extern JS_PUBLIC_API(void)
+JS_TracerInit(JSTracer *trc, JSRuntime *rt, JSTraceCallback callback);
+
+extern JS_PUBLIC_API(void)
+JS_TraceChildren(JSTracer *trc, void *thing, JSGCTraceKind kind);
+
+extern JS_PUBLIC_API(void)
+JS_TraceRuntime(JSTracer *trc);
+
+extern JS_PUBLIC_API(void)
+JS_GetTraceThingInfo(char *buf, size_t bufsize, JSTracer *trc,
+                     void *thing, JSGCTraceKind kind, bool includeDetails);
+
+extern JS_PUBLIC_API(const char *)
+JS_GetTraceEdgeName(JSTracer *trc, char *buffer, int bufferSize);
+
+#endif /* js_Tracer_h */
--- a/js/src/builtin/RegExp.cpp
+++ b/js/src/builtin/RegExp.cpp
@@ -264,21 +264,17 @@ CompileRegExpObject(JSContext *cx, RegEx
         return true;
     }
 
     RootedAtom source(cx);
     if (sourceValue.isUndefined()) {
         source = cx->runtime()->emptyString;
     } else {
         /* Coerce to string and compile. */
-        JSString *str = ToString<CanGC>(cx, sourceValue);
-        if (!str)
-            return false;
-
-        source = AtomizeString<CanGC>(cx, str);
+        source = ToAtom<CanGC>(cx, sourceValue);
         if (!source)
             return false;
     }
 
     RegExpFlag flags = RegExpFlag(0);
     if (args.hasDefined(1)) {
         RootedString flagStr(cx, ToString<CanGC>(cx, args[1]));
         if (!flagStr)
--- a/js/src/frontend/FoldConstants.cpp
+++ b/js/src/frontend/FoldConstants.cpp
@@ -81,20 +81,17 @@ FoldType(ExclusiveContext *cx, ParseNode
                 pn->pn_dval = d;
                 pn->setKind(PNK_NUMBER);
                 pn->setOp(JSOP_DOUBLE);
             }
             break;
 
           case PNK_STRING:
             if (pn->isKind(PNK_NUMBER)) {
-                JSString *str = js_NumberToString<CanGC>(cx, pn->pn_dval);
-                if (!str)
-                    return false;
-                pn->pn_atom = AtomizeString<CanGC>(cx, str);
+                pn->pn_atom = NumberToAtom<CanGC>(cx, pn->pn_dval);
                 if (!pn->pn_atom)
                     return false;
                 pn->setKind(PNK_STRING);
                 pn->setOp(JSOP_STRING);
             }
             break;
 
           default:;
--- a/js/src/gc/Barrier-inl.h
+++ b/js/src/gc/Barrier-inl.h
@@ -13,25 +13,16 @@
 
 #include "gc/Marking.h"
 #include "gc/StoreBuffer.h"
 
 #include "vm/String-inl.h"
 
 namespace js {
 
-JS_ALWAYS_INLINE JS::Zone *
-ZoneOfValue(const JS::Value &value)
-{
-    JS_ASSERT(value.isMarkable());
-    if (value.isObject())
-        return value.toObject().zone();
-    return static_cast<js::gc::Cell *>(value.toGCThing())->tenuredZone();
-}
-
 template <typename T, typename Unioned>
 void
 EncapsulatedPtr<T, Unioned>::pre()
 {
     T::writeBarrierPre(value);
 }
 
 template <typename T>
@@ -48,363 +39,16 @@ template <typename T>
 inline void
 RelocatablePtr<T>::relocate(JSRuntime *rt)
 {
 #ifdef JSGC_GENERATIONAL
     T::writeBarrierPostRemove(this->value, &this->value);
 #endif
 }
 
-inline
-EncapsulatedValue::~EncapsulatedValue()
-{
-    pre();
-}
-
-inline EncapsulatedValue &
-EncapsulatedValue::operator=(const Value &v)
-{
-    pre();
-    JS_ASSERT(!IsPoisonedValue(v));
-    value = v;
-    return *this;
-}
-
-inline EncapsulatedValue &
-EncapsulatedValue::operator=(const EncapsulatedValue &v)
-{
-    pre();
-    JS_ASSERT(!IsPoisonedValue(v));
-    value = v.get();
-    return *this;
-}
-
-inline void
-EncapsulatedValue::writeBarrierPre(const Value &value)
-{
-#ifdef JSGC_INCREMENTAL
-    if (value.isMarkable() && runtimeFromAnyThread(value)->needsBarrier())
-        writeBarrierPre(ZoneOfValue(value), value);
-#endif
-}
-
-inline void
-EncapsulatedValue::writeBarrierPre(Zone *zone, const Value &value)
-{
-#ifdef JSGC_INCREMENTAL
-    if (zone->needsBarrier()) {
-        JS_ASSERT_IF(value.isMarkable(), runtimeFromMainThread(value)->needsBarrier());
-        Value tmp(value);
-        js::gc::MarkValueUnbarriered(zone->barrierTracer(), &tmp, "write barrier");
-        JS_ASSERT(tmp == value);
-    }
-#endif
-}
-
-inline void
-EncapsulatedValue::pre()
-{
-    writeBarrierPre(value);
-}
-
-inline void
-EncapsulatedValue::pre(Zone *zone)
-{
-    writeBarrierPre(zone, value);
-}
-
-inline
-HeapValue::HeapValue()
-    : EncapsulatedValue(UndefinedValue())
-{
-    post();
-}
-
-inline
-HeapValue::HeapValue(const Value &v)
-    : EncapsulatedValue(v)
-{
-    JS_ASSERT(!IsPoisonedValue(v));
-    post();
-}
-
-inline
-HeapValue::HeapValue(const HeapValue &v)
-    : EncapsulatedValue(v.value)
-{
-    JS_ASSERT(!IsPoisonedValue(v.value));
-    post();
-}
-
-inline
-HeapValue::~HeapValue()
-{
-    pre();
-}
-
-inline void
-HeapValue::init(const Value &v)
-{
-    JS_ASSERT(!IsPoisonedValue(v));
-    value = v;
-    post();
-}
-
-inline void
-HeapValue::init(JSRuntime *rt, const Value &v)
-{
-    JS_ASSERT(!IsPoisonedValue(v));
-    value = v;
-    post(rt);
-}
-
-inline HeapValue &
-HeapValue::operator=(const Value &v)
-{
-    pre();
-    JS_ASSERT(!IsPoisonedValue(v));
-    value = v;
-    post();
-    return *this;
-}
-
-inline HeapValue &
-HeapValue::operator=(const HeapValue &v)
-{
-    pre();
-    JS_ASSERT(!IsPoisonedValue(v.value));
-    value = v.value;
-    post();
-    return *this;
-}
-
-inline void
-HeapValue::set(Zone *zone, const Value &v)
-{
-#ifdef DEBUG
-    if (value.isMarkable()) {
-        JS_ASSERT(ZoneOfValue(value) == zone ||
-                  zone->runtimeFromAnyThread()->isAtomsZone(ZoneOfValue(value)));
-    }
-#endif
-
-    pre(zone);
-    JS_ASSERT(!IsPoisonedValue(v));
-    value = v;
-    post(zone->runtimeFromAnyThread());
-}
-
-inline void
-HeapValue::writeBarrierPost(const Value &value, Value *addr)
-{
-#ifdef JSGC_GENERATIONAL
-    if (value.isMarkable())
-        runtimeFromMainThread(value)->gcStoreBuffer.putValue(addr);
-#endif
-}
-
-inline void
-HeapValue::writeBarrierPost(JSRuntime *rt, const Value &value, Value *addr)
-{
-#ifdef JSGC_GENERATIONAL
-    if (value.isMarkable())
-        rt->gcStoreBuffer.putValue(addr);
-#endif
-}
-
-inline void
-HeapValue::post()
-{
-    writeBarrierPost(value, &value);
-}
-
-inline void
-HeapValue::post(JSRuntime *rt)
-{
-    writeBarrierPost(rt, value, &value);
-}
-
-inline
-RelocatableValue::RelocatableValue()
-    : EncapsulatedValue(UndefinedValue())
-{
-}
-
-inline
-RelocatableValue::RelocatableValue(const Value &v)
-    : EncapsulatedValue(v)
-{
-    JS_ASSERT(!IsPoisonedValue(v));
-    if (v.isMarkable())
-        post();
-}
-
-inline
-RelocatableValue::RelocatableValue(const RelocatableValue &v)
-    : EncapsulatedValue(v.value)
-{
-    JS_ASSERT(!IsPoisonedValue(v.value));
-    if (v.value.isMarkable())
-        post();
-}
-
-inline
-RelocatableValue::~RelocatableValue()
-{
-    if (value.isMarkable())
-        relocate(runtimeFromMainThread(value));
-}
-
-inline RelocatableValue &
-RelocatableValue::operator=(const Value &v)
-{
-    pre();
-    JS_ASSERT(!IsPoisonedValue(v));
-    if (v.isMarkable()) {
-        value = v;
-        post();
-    } else if (value.isMarkable()) {
-        JSRuntime *rt = runtimeFromMainThread(value);
-        relocate(rt);
-        value = v;
-    } else {
-        value = v;
-    }
-    return *this;
-}
-
-inline RelocatableValue &
-RelocatableValue::operator=(const RelocatableValue &v)
-{
-    pre();
-    JS_ASSERT(!IsPoisonedValue(v.value));
-    if (v.value.isMarkable()) {
-        value = v.value;
-        post();
-    } else if (value.isMarkable()) {
-        JSRuntime *rt = runtimeFromMainThread(value);
-        relocate(rt);
-        value = v.value;
-    } else {
-        value = v.value;
-    }
-    return *this;
-}
-
-inline void
-RelocatableValue::post()
-{
-#ifdef JSGC_GENERATIONAL
-    JS_ASSERT(value.isMarkable());
-    runtimeFromMainThread(value)->gcStoreBuffer.putRelocatableValue(&value);
-#endif
-}
-
-inline void
-RelocatableValue::relocate(JSRuntime *rt)
-{
-#ifdef JSGC_GENERATIONAL
-    rt->gcStoreBuffer.removeRelocatableValue(&value);
-#endif
-}
-
-inline
-HeapSlot::HeapSlot(JSObject *obj, Kind kind, uint32_t slot, const Value &v)
-    : EncapsulatedValue(v)
-{
-    JS_ASSERT(!IsPoisonedValue(v));
-    post(obj, kind, slot, v);
-}
-
-inline
-HeapSlot::HeapSlot(JSObject *obj, Kind kind, uint32_t slot, const HeapSlot &s)
-    : EncapsulatedValue(s.value)
-{
-    JS_ASSERT(!IsPoisonedValue(s.value));
-    post(obj, kind, slot, s);
-}
-
-inline
-HeapSlot::~HeapSlot()
-{
-    pre();
-}
-
-inline void
-HeapSlot::init(JSObject *obj, Kind kind, uint32_t slot, const Value &v)
-{
-    value = v;
-    post(obj, kind, slot, v);
-}
-
-inline void
-HeapSlot::init(JSRuntime *rt, JSObject *obj, Kind kind, uint32_t slot, const Value &v)
-{
-    value = v;
-    post(rt, obj, kind, slot, v);
-}
-
-inline void
-HeapSlot::set(JSObject *obj, Kind kind, uint32_t slot, const Value &v)
-{
-    JS_ASSERT_IF(kind == Slot, &obj->getSlotRef(slot) == this);
-    JS_ASSERT_IF(kind == Element, &obj->getDenseElement(slot) == (const Value *)this);
-
-    pre();
-    JS_ASSERT(!IsPoisonedValue(v));
-    value = v;
-    post(obj, kind, slot, v);
-}
-
-inline void
-HeapSlot::set(Zone *zone, JSObject *obj, Kind kind, uint32_t slot, const Value &v)
-{
-    JS_ASSERT_IF(kind == Slot, &obj->getSlotRef(slot) == this);
-    JS_ASSERT_IF(kind == Element, &obj->getDenseElement(slot) == (const Value *)this);
-    JS_ASSERT(obj->zone() == zone);
-
-    pre(zone);
-    JS_ASSERT(!IsPoisonedValue(v));
-    value = v;
-    post(zone->runtimeFromAnyThread(), obj, kind, slot, v);
-}
-
-inline void
-HeapSlot::writeBarrierPost(JSObject *obj, Kind kind, uint32_t slot, Value target)
-{
-#ifdef JSGC_GENERATIONAL
-    writeBarrierPost(obj->runtimeFromAnyThread(), obj, kind, slot, target);
-#endif
-}
-
-inline void
-HeapSlot::writeBarrierPost(JSRuntime *rt, JSObject *obj, Kind kind, uint32_t slot, Value target)
-{
-#ifdef JSGC_GENERATIONAL
-    JS_ASSERT_IF(kind == Slot, obj->getSlotAddressUnchecked(slot)->get() == target);
-    JS_ASSERT_IF(kind == Element,
-                 static_cast<HeapSlot *>(obj->getDenseElements() + slot)->get() == target);
-
-    if (target.isObject())
-        rt->gcStoreBuffer.putSlot(obj, kind, slot, &target.toObject());
-#endif
-}
-
-inline void
-HeapSlot::post(JSObject *owner, Kind kind, uint32_t slot, Value target)
-{
-    HeapSlot::writeBarrierPost(owner, kind, slot, target);
-}
-
-inline void
-HeapSlot::post(JSRuntime *rt, JSObject *owner, Kind kind, uint32_t slot, Value target)
-{
-    HeapSlot::writeBarrierPost(rt, owner, kind, slot, target);
-}
-
 #ifdef JSGC_GENERATIONAL
 class DenseRangeRef : public gc::BufferableRef
 {
     JSObject *owner;
     uint32_t start;
     uint32_t end;
 
   public:
@@ -444,17 +88,16 @@ inline void
 HashTableWriteBarrierPost(JSRuntime *rt, Map *map, const Key &key)
 {
 #ifdef JSGC_GENERATIONAL
     if (key && IsInsideNursery(rt, key))
         rt->gcStoreBuffer.putGeneric(gc::HashKeyRef<Map, Key>(map, key));
 #endif
 }
 
-
 inline
 EncapsulatedId::~EncapsulatedId()
 {
     pre();
 }
 
 inline EncapsulatedId &
 EncapsulatedId::operator=(const EncapsulatedId &v)
new file mode 100644
--- /dev/null
+++ b/js/src/gc/Barrier.cpp
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * 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 "gc/Barrier.h"
+
+#include "jsobj.h"
+
+#include "vm/ObjectImpl-inl.h"
+
+namespace js {
+
+#ifdef DEBUG
+
+bool
+HeapValue::preconditionForSet(Zone *zone)
+{
+    if (!value.isMarkable())
+        return true;
+
+    return ZoneOfValue(value) == zone ||
+           zone->runtimeFromAnyThread()->isAtomsZone(ZoneOfValue(value));
+}
+
+bool
+HeapSlot::preconditionForSet(JSObject *owner, Kind kind, uint32_t slot)
+{
+    return kind == Slot
+         ? &owner->getSlotRef(slot) == this
+         : &owner->getDenseElement(slot) == (const Value *)this;
+}
+
+bool
+HeapSlot::preconditionForSet(Zone *zone, JSObject *owner, Kind kind, uint32_t slot)
+{
+    bool ok = kind == Slot
+            ? &owner->getSlotRef(slot) == this
+            : &owner->getDenseElement(slot) == (const Value *)this;
+    return ok && owner->zone() == zone;
+}
+
+void
+HeapSlot::preconditionForWriteBarrierPost(JSObject *obj, Kind kind, uint32_t slot, Value target)
+{
+    JS_ASSERT_IF(kind == Slot, obj->getSlotAddressUnchecked(slot)->get() == target);
+    JS_ASSERT_IF(kind == Element,
+                 static_cast<HeapSlot *>(obj->getDenseElements() + slot)->get() == target);
+}
+
+#endif // DEBUG
+
+} // namespace js
--- a/js/src/gc/Barrier.h
+++ b/js/src/gc/Barrier.h
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef gc_Barrier_h
 #define gc_Barrier_h
 
 #include "NamespaceImports.h"
 
 #include "gc/Heap.h"
+#include "gc/StoreBuffer.h"
 #include "js/HashTable.h"
 #include "js/Id.h"
 #include "js/RootingAPI.h"
 
 /*
  * A write barrier is a mechanism used by incremental or generation GCs to
  * ensure that every value that needs to be marked is marked. In general, the
  * write barrier should be invoked whenever a write can cause the set of things
@@ -114,16 +115,34 @@
  * the init naming idiom in many places to signify that a field is being
  * assigned for the first time.
  */
 
 namespace js {
 
 class PropertyName;
 
+namespace gc {
+// Direct value access used by the write barriers and the jits.
+void
+MarkValueUnbarriered(JSTracer *trc, Value *v, const char *name);
+}
+
+JS::Zone *
+ZoneOfObject(const JSObject &obj);
+
+JS_ALWAYS_INLINE JS::Zone *
+ZoneOfValue(const JS::Value &value)
+{
+    JS_ASSERT(value.isMarkable());
+    if (value.isObject())
+        return ZoneOfObject(value.toObject());
+    return static_cast<js::gc::Cell *>(value.toGCThing())->tenuredZone();
+}
+
 template<class T, typename Unioned = uintptr_t>
 class EncapsulatedPtr
 {
   protected:
     union {
         T *value;
         Unioned other;
     };
@@ -387,106 +406,273 @@ class EncapsulatedValue : public ValueOp
 
   public:
     EncapsulatedValue(const Value &v) : value(v) {
         JS_ASSERT(!IsPoisonedValue(v));
     }
     EncapsulatedValue(const EncapsulatedValue &v) : value(v) {
         JS_ASSERT(!IsPoisonedValue(v));
     }
-    inline ~EncapsulatedValue();
+
+    ~EncapsulatedValue() {
+        pre();
+    }
 
     void init(const Value &v) {
         JS_ASSERT(!IsPoisonedValue(v));
         value = v;
     }
     void init(JSRuntime *rt, const Value &v) {
         JS_ASSERT(!IsPoisonedValue(v));
         value = v;
     }
 
-    inline EncapsulatedValue &operator=(const Value &v);
-    inline EncapsulatedValue &operator=(const EncapsulatedValue &v);
+    EncapsulatedValue &operator=(const Value &v) {
+        pre();
+        JS_ASSERT(!IsPoisonedValue(v));
+        value = v;
+        return *this;
+    }
+
+    EncapsulatedValue &operator=(const EncapsulatedValue &v) {
+        pre();
+        JS_ASSERT(!IsPoisonedValue(v));
+        value = v.get();
+        return *this;
+    }
 
     bool operator==(const EncapsulatedValue &v) const { return value == v.value; }
     bool operator!=(const EncapsulatedValue &v) const { return value != v.value; }
 
     const Value &get() const { return value; }
     Value *unsafeGet() { return &value; }
     operator const Value &() const { return value; }
 
     JSGCTraceKind gcKind() const { return value.gcKind(); }
 
     uint64_t asRawBits() const { return value.asRawBits(); }
 
-    static inline void writeBarrierPre(const Value &v);
-    static inline void writeBarrierPre(Zone *zone, const Value &v);
+    static void writeBarrierPre(const Value &v) {
+#ifdef JSGC_INCREMENTAL
+        if (v.isMarkable() && shadowRuntimeFromAnyThread(v)->needsBarrier())
+            writeBarrierPre(ZoneOfValue(v), v);
+#endif
+    }
+
+    static void writeBarrierPre(Zone *zone, const Value &v) {
+#ifdef JSGC_INCREMENTAL
+        JS::shadow::Zone *shadowZone = JS::shadow::Zone::asShadowZone(zone);
+        if (shadowZone->needsBarrier()) {
+            JS_ASSERT_IF(v.isMarkable(), shadowRuntimeFromMainThread(v)->needsBarrier());
+            Value tmp(v);
+            js::gc::MarkValueUnbarriered(shadowZone->barrierTracer(), &tmp, "write barrier");
+            JS_ASSERT(tmp == v);
+        }
+#endif
+    }
 
   protected:
-    inline void pre();
-    inline void pre(Zone *zone);
+    void pre() { writeBarrierPre(value); }
+    void pre(Zone *zone) { writeBarrierPre(zone, value); }
 
-    static inline JSRuntime *runtimeFromMainThread(const Value &v) {
+    static JSRuntime *runtimeFromMainThread(const Value &v) {
         JS_ASSERT(v.isMarkable());
         return static_cast<js::gc::Cell *>(v.toGCThing())->runtimeFromMainThread();
     }
-    static inline JSRuntime *runtimeFromAnyThread(const Value &v) {
+    static JSRuntime *runtimeFromAnyThread(const Value &v) {
         JS_ASSERT(v.isMarkable());
         return static_cast<js::gc::Cell *>(v.toGCThing())->runtimeFromAnyThread();
     }
+    static JS::shadow::Runtime *shadowRuntimeFromMainThread(const Value &v) {
+        return reinterpret_cast<JS::shadow::Runtime*>(runtimeFromMainThread(v));
+    }
+    static JS::shadow::Runtime *shadowRuntimeFromAnyThread(const Value &v) {
+        return reinterpret_cast<JS::shadow::Runtime*>(runtimeFromAnyThread(v));
+    }
 
   private:
     friend class ValueOperations<EncapsulatedValue>;
     const Value * extract() const { return &value; }
 };
 
 class HeapValue : public EncapsulatedValue
 {
   public:
-    explicit inline HeapValue();
-    explicit inline HeapValue(const Value &v);
-    explicit inline HeapValue(const HeapValue &v);
-    inline ~HeapValue();
+    explicit HeapValue()
+      : EncapsulatedValue(UndefinedValue())
+    {
+        post();
+    }
+
+    explicit HeapValue(const Value &v)
+      : EncapsulatedValue(v)
+    {
+        JS_ASSERT(!IsPoisonedValue(v));
+        post();
+    }
+
+    explicit HeapValue(const HeapValue &v)
+      : EncapsulatedValue(v.value)
+    {
+        JS_ASSERT(!IsPoisonedValue(v.value));
+        post();
+    }
+
+    ~HeapValue() {
+        pre();
+    }
 
-    inline void init(const Value &v);
-    inline void init(JSRuntime *rt, const Value &v);
+    void init(const Value &v) {
+        JS_ASSERT(!IsPoisonedValue(v));
+        value = v;
+        post();
+    }
+
+    void init(JSRuntime *rt, const Value &v) {
+        JS_ASSERT(!IsPoisonedValue(v));
+        value = v;
+        post(rt);
+    }
 
-    inline HeapValue &operator=(const Value &v);
-    inline HeapValue &operator=(const HeapValue &v);
+    HeapValue &operator=(const Value &v) {
+        pre();
+        JS_ASSERT(!IsPoisonedValue(v));
+        value = v;
+        post();
+        return *this;
+    }
+
+    HeapValue &operator=(const HeapValue &v) {
+        pre();
+        JS_ASSERT(!IsPoisonedValue(v.value));
+        value = v.value;
+        post();
+        return *this;
+    }
+
+#ifdef DEBUG
+    bool preconditionForSet(Zone *zone);
+#endif
 
     /*
      * This is a faster version of operator=. Normally, operator= has to
      * determine the compartment of the value before it can decide whether to do
      * the barrier. If you already know the compartment, it's faster to pass it
      * in.
      */
-    inline void set(Zone *zone, const Value &v);
+    void set(Zone *zone, const Value &v) {
+        JS::shadow::Zone *shadowZone = JS::shadow::Zone::asShadowZone(zone);
+        JS_ASSERT(preconditionForSet(zone));
+        pre(zone);
+        JS_ASSERT(!IsPoisonedValue(v));
+        value = v;
+        post(shadowZone->runtimeFromAnyThread());
+    }
 
-    static inline void writeBarrierPost(const Value &v, Value *addr);
-    static inline void writeBarrierPost(JSRuntime *rt, const Value &v, Value *addr);
+    static void writeBarrierPost(const Value &value, Value *addr) {
+#ifdef JSGC_GENERATIONAL
+        if (value.isMarkable())
+            shadowRuntimeFromMainThread(value)->gcStoreBufferPtr()->putValue(addr);
+#endif
+    }
+
+    static void writeBarrierPost(JSRuntime *rt, const Value &value, Value *addr) {
+#ifdef JSGC_GENERATIONAL
+        if (value.isMarkable()) {
+            JS::shadow::Runtime *shadowRuntime = JS::shadow::Runtime::asShadowRuntime(rt);
+            shadowRuntime->gcStoreBufferPtr()->putValue(addr);
+        }
+#endif
+    }
 
   private:
-    inline void post();
-    inline void post(JSRuntime *rt);
+    void post() {
+        writeBarrierPost(value, &value);
+    }
+
+    void post(JSRuntime *rt) {
+        writeBarrierPost(rt, value, &value);
+    }
 };
 
 class RelocatableValue : public EncapsulatedValue
 {
   public:
-    explicit inline RelocatableValue();
-    explicit inline RelocatableValue(const Value &v);
-    inline RelocatableValue(const RelocatableValue &v);
-    inline ~RelocatableValue();
+    explicit RelocatableValue()
+      : EncapsulatedValue(UndefinedValue())
+    {}
+
+    explicit RelocatableValue(const Value &v)
+      : EncapsulatedValue(v)
+    {
+        JS_ASSERT(!IsPoisonedValue(v));
+        if (v.isMarkable())
+            post();
+    }
+
+    RelocatableValue(const RelocatableValue &v)
+      : EncapsulatedValue(v.value)
+    {
+        JS_ASSERT(!IsPoisonedValue(v.value));
+        if (v.value.isMarkable())
+            post();
+    }
+
+    ~RelocatableValue()
+    {
+        if (value.isMarkable())
+            relocate(runtimeFromMainThread(value));
+    }
 
-    inline RelocatableValue &operator=(const Value &v);
-    inline RelocatableValue &operator=(const RelocatableValue &v);
+    RelocatableValue &operator=(const Value &v) {
+        pre();
+        JS_ASSERT(!IsPoisonedValue(v));
+        if (v.isMarkable()) {
+            value = v;
+            post();
+        } else if (value.isMarkable()) {
+            JSRuntime *rt = runtimeFromMainThread(value);
+            relocate(rt);
+            value = v;
+        } else {
+            value = v;
+        }
+        return *this;
+    }
+
+    RelocatableValue &operator=(const RelocatableValue &v) {
+        pre();
+        JS_ASSERT(!IsPoisonedValue(v.value));
+        if (v.value.isMarkable()) {
+            value = v.value;
+            post();
+        } else if (value.isMarkable()) {
+            JSRuntime *rt = runtimeFromMainThread(value);
+            relocate(rt);
+            value = v.value;
+        } else {
+            value = v.value;
+        }
+        return *this;
+    }
 
   private:
-    inline void post();
-    inline void relocate(JSRuntime *rt);
+    void post() {
+#ifdef JSGC_GENERATIONAL
+        JS_ASSERT(value.isMarkable());
+        shadowRuntimeFromMainThread(value)->gcStoreBufferPtr()->putRelocatableValue(&value);
+#endif
+    }
+
+    void relocate(JSRuntime *rt) {
+#ifdef JSGC_GENERATIONAL
+        JS::shadow::Runtime *shadowRuntime = JS::shadow::Runtime::asShadowRuntime(rt);
+        shadowRuntime->gcStoreBufferPtr()->removeRelocatableValue(&value);
+#endif
+    }
 };
 
 class HeapSlot : public EncapsulatedValue
 {
     /*
      * Operator= is not valid for HeapSlot because is must take the object and
      * slot offset to provide to the post/generational barrier.
      */
@@ -495,34 +681,100 @@ class HeapSlot : public EncapsulatedValu
     inline HeapSlot &operator=(const HeapSlot &v) MOZ_DELETE;
 
   public:
     enum Kind {
         Slot,
         Element
     };
 
-    explicit inline HeapSlot() MOZ_DELETE;
-    explicit inline HeapSlot(JSObject *obj, Kind kind, uint32_t slot, const Value &v);
-    explicit inline HeapSlot(JSObject *obj, Kind kind, uint32_t slot, const HeapSlot &v);
-    inline ~HeapSlot();
+    explicit HeapSlot() MOZ_DELETE;
+
+    explicit HeapSlot(JSObject *obj, Kind kind, uint32_t slot, const Value &v)
+      : EncapsulatedValue(v)
+    {
+        JS_ASSERT(!IsPoisonedValue(v));
+        post(obj, kind, slot, v);
+    }
+
+    explicit HeapSlot(JSObject *obj, Kind kind, uint32_t slot, const HeapSlot &s)
+      : EncapsulatedValue(s.value)
+    {
+        JS_ASSERT(!IsPoisonedValue(s.value));
+        post(obj, kind, slot, s);
+    }
+
+    ~HeapSlot() {
+        pre();
+    }
+
+    void init(JSObject *owner, Kind kind, uint32_t slot, const Value &v) {
+        value = v;
+        post(owner, kind, slot, v);
+    }
+
+    void init(JSRuntime *rt, JSObject *owner, Kind kind, uint32_t slot, const Value &v) {
+        value = v;
+        post(rt, owner, kind, slot, v);
+    }
+
+#ifdef DEBUG
+    bool preconditionForSet(JSObject *owner, Kind kind, uint32_t slot);
+    bool preconditionForSet(Zone *zone, JSObject *owner, Kind kind, uint32_t slot);
+    static void preconditionForWriteBarrierPost(JSObject *obj, Kind kind, uint32_t slot,
+                                                Value target);
+#endif
 
-    inline void init(JSObject *owner, Kind kind, uint32_t slot, const Value &v);
-    inline void init(JSRuntime *rt, JSObject *owner, Kind kind, uint32_t slot, const Value &v);
+    void set(JSObject *owner, Kind kind, uint32_t slot, const Value &v) {
+        JS_ASSERT(preconditionForSet(owner, kind, slot));
+        pre();
+        JS_ASSERT(!IsPoisonedValue(v));
+        value = v;
+        post(owner, kind, slot, v);
+    }
+
+    void set(Zone *zone, JSObject *owner, Kind kind, uint32_t slot, const Value &v) {
+        JS_ASSERT(preconditionForSet(zone, owner, kind, slot));
+        JS::shadow::Zone *shadowZone = JS::shadow::Zone::asShadowZone(zone);
+        pre(zone);
+        JS_ASSERT(!IsPoisonedValue(v));
+        value = v;
+        post(shadowZone->runtimeFromAnyThread(), owner, kind, slot, v);
+    }
 
-    inline void set(JSObject *owner, Kind kind, uint32_t slot, const Value &v);
-    inline void set(Zone *zone, JSObject *owner, Kind kind, uint32_t slot, const Value &v);
+    static void writeBarrierPost(JSObject *obj, Kind kind, uint32_t slot, Value target)
+    {
+#ifdef JSGC_GENERATIONAL
+        js::gc::Cell *cell = reinterpret_cast<js::gc::Cell*>(obj);
+        writeBarrierPost(cell->runtimeFromAnyThread(), obj, kind, slot, target);
+#endif
+    }
 
-    static inline void writeBarrierPost(JSObject *obj, Kind kind, uint32_t slot, Value target);
-    static inline void writeBarrierPost(JSRuntime *rt, JSObject *obj, Kind kind, uint32_t slot,
-                                        Value target);
+    static void writeBarrierPost(JSRuntime *rt, JSObject *obj, Kind kind, uint32_t slot,
+                                 Value target)
+    {
+#ifdef DEBUG
+        preconditionForWriteBarrierPost(obj, kind, slot, target);
+#endif
+#ifdef JSGC_GENERATIONAL
+        if (target.isObject()) {
+            JS::shadow::Runtime *shadowRuntime = JS::shadow::Runtime::asShadowRuntime(rt);
+            shadowRuntime->gcStoreBufferPtr()->putSlot(obj, kind, slot, &target.toObject());
+        }
+#endif
+    }
 
   private:
-    inline void post(JSObject *owner, Kind kind, uint32_t slot, Value target);
-    inline void post(JSRuntime *rt, JSObject *owner, Kind kind, uint32_t slot, Value target);
+    void post(JSObject *owner, Kind kind, uint32_t slot, Value target) {
+        HeapSlot::writeBarrierPost(owner, kind, slot, target);
+    }
+
+    void post(JSRuntime *rt, JSObject *owner, Kind kind, uint32_t slot, Value target) {
+        HeapSlot::writeBarrierPost(rt, owner, kind, slot, target);
+    }
 };
 
 /*
  * NOTE: This is a placeholder for bug 619558.
  *
  * Run a post write barrier that encompasses multiple contiguous slots in a
  * single step.
  */
--- a/js/src/gc/Heap.h
+++ b/js/src/gc/Heap.h
@@ -17,27 +17,26 @@
 #include "jstypes.h"
 #include "jsutil.h"
 
 #include "ds/BitArray.h"
 #include "js/HeapAPI.h"
 
 struct JSCompartment;
 
-extern "C" {
 struct JSRuntime;
+
+namespace JS {
+namespace shadow {
+class Runtime;
+}
 }
 
 namespace js {
 
-// Whether the current thread is permitted access to any part of the specified
-// runtime or zone.
-extern bool CurrentThreadCanAccessRuntime(JSRuntime *rt);
-extern bool CurrentThreadCanAccessZone(JS::Zone *zone);
-
 class FreeOp;
 
 namespace gc {
 
 struct Arena;
 struct ArenaHeader;
 struct Chunk;
 
@@ -101,16 +100,17 @@ struct Cell
 
     inline JSRuntime *runtimeFromMainThread() const;
     inline JS::Zone *tenuredZone() const;
     inline bool tenuredIsInsideZone(JS::Zone *zone) const;
 
     // Note: Unrestricted access to the runtime of a GC thing from an arbitrary
     // thread can easily lead to races. Use this method very carefully.
     inline JSRuntime *runtimeFromAnyThread() const;
+    inline JS::shadow::Runtime *shadowRuntimeFromAnyThread() const;
 
 #ifdef DEBUG
     inline bool isAligned() const;
     inline bool isTenured() const;
 #endif
 
   protected:
     inline uintptr_t address() const;
@@ -964,16 +964,22 @@ Cell::runtimeFromMainThread() const
 }
 
 inline JSRuntime *
 Cell::runtimeFromAnyThread() const
 {
     return chunk()->info.runtime;
 }
 
+inline JS::shadow::Runtime *
+Cell::shadowRuntimeFromAnyThread() const
+{
+    return reinterpret_cast<JS::shadow::Runtime*>(runtimeFromAnyThread());
+}
+
 AllocKind
 Cell::tenuredGetAllocKind() const
 {
     return arenaHeader()->getAllocKind();
 }
 
 bool
 Cell::isMarked(uint32_t color /* = BLACK */) const
--- a/js/src/gc/Marking.h
+++ b/js/src/gc/Marking.h
@@ -218,20 +218,16 @@ MarkCrossCompartmentSlot(JSTracer *trc, 
 
 /*
  * The unioned HeapPtr stored in script->globalObj needs special treatment to
  * typecheck correctly.
  */
 void
 MarkObject(JSTracer *trc, HeapPtr<GlobalObject, JSScript *> *thingp, const char *name);
 
-/* Direct value access used by the write barriers and the methodjit. */
-void
-MarkValueUnbarriered(JSTracer *trc, Value *v, const char *name);
-
 /*
  * MarkChildren<JSObject> is exposed solely for preWriteBarrier on
  * JSObject::TradeGuts. It should not be considered external interface.
  */
 void
 MarkChildren(JSTracer *trc, JSObject *obj);
 
 /*
--- a/js/src/gc/Memory.cpp
+++ b/js/src/gc/Memory.cpp
@@ -138,17 +138,17 @@ gc::UnmapPages(JSRuntime *rt, void *addr
 
     uintptr_t base = reinterpret_cast<uintptr_t>(addr) - ((2 * size) - cb);
     DosFreeMem(reinterpret_cast<void*>(base));
 
     return;
 }
 
 static void *
-gc::MapAlignedPagesRecursively(JSRuntime *rt, size_t size, size_t alignment, int& recursions)
+MapAlignedPagesRecursively(JSRuntime *rt, size_t size, size_t alignment, int& recursions)
 {
     if (++recursions >= OS2_MAX_RECURSIONS)
         return NULL;
 
     void *tmp;
     if (DosAllocMem(&tmp, size,
                     OBJ_ANY | PAG_COMMIT | PAG_READ | PAG_WRITE)) {
         JS_ALWAYS_TRUE(DosAllocMem(&tmp, size,
@@ -304,35 +304,72 @@ gc::GetPageFaultCount()
 #include <unistd.h>
 
 void
 gc::InitMemorySubsystem(JSRuntime *rt)
 {
     rt->gcSystemPageSize = rt->gcSystemAllocGranularity = size_t(sysconf(_SC_PAGESIZE));
 }
 
+static inline void *
+MapMemory(size_t length, int prot, int flags, int fd, off_t offset)
+{
+#if defined(__ia64__)
+    /*
+     * The JS engine assumes that all allocated pointers have their high 17 bits clear,
+     * which ia64's mmap doesn't support directly. However, we can emulate it by passing
+     * mmap an "addr" parameter with those bits clear. The mmap will return that address,
+     * or the nearest available memory above that address, providing a near-guarantee
+     * that those bits are clear. If they are not, we return NULL below to indicate
+     * out-of-memory.
+     *
+     * The addr is chosen as 0x0000070000000000, which still allows about 120TB of virtual
+     * address space.
+     *
+     * See Bug 589735 for more information.
+     */
+    void *region = mmap((void*)0x0000070000000000, length, prot, flags, fd, offset);
+    if (region == MAP_FAILED)
+        return MAP_FAILED;
+    /* 
+     * If the allocated memory doesn't have its upper 17 bits clear, consider it
+     * as out of memory.
+     */
+    if ((uintptr_t(region) + (length - 1)) & 0xffff800000000000) {
+        JS_ALWAYS_TRUE(0 == munmap(region, length));
+        return MAP_FAILED;
+    }
+    return region;
+#else
+    return mmap(NULL, length, prot, flags, fd, offset);
+#endif
+}
+
 void *
 gc::MapAlignedPages(JSRuntime *rt, size_t size, size_t alignment)
 {
     JS_ASSERT(size >= alignment);
     JS_ASSERT(size % alignment == 0);
     JS_ASSERT(size % rt->gcSystemPageSize == 0);
     JS_ASSERT(alignment % rt->gcSystemAllocGranularity == 0);
 
     int prot = PROT_READ | PROT_WRITE;
     int flags = MAP_PRIVATE | MAP_ANON;
 
     /* Special case: If we want page alignment, no further work is needed. */
     if (alignment == rt->gcSystemAllocGranularity) {
-        return mmap(NULL, size, prot, flags, -1, 0);
+        void *region = MapMemory(size, prot, flags, -1, 0);
+        if (region == MAP_FAILED)
+            return NULL;
+        return region;
     }
 
     /* Overallocate and unmap the region's edges. */
     size_t reqSize = Min(size + 2 * alignment, 2 * size);
-    void *region = mmap(NULL, reqSize, prot, flags, -1, 0);
+    void *region = MapMemory(reqSize, prot, flags, -1, 0);
     if (region == MAP_FAILED)
         return NULL;
 
     uintptr_t regionEnd = uintptr_t(region) + reqSize;
     uintptr_t offset = uintptr_t(region) % alignment;
     JS_ASSERT(offset < reqSize - size);
 
     void *front = (void *)(uintptr_t(region) + (alignment - offset));
--- a/js/src/gc/Nursery.h
+++ b/js/src/gc/Nursery.h
@@ -5,27 +5,37 @@
  * 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 gc_Nursery_h
 #define gc_Nursery_h
 
 #ifdef JSGC_GENERATIONAL
 
-#include "jsgc.h"
+#include "jsalloc.h"
 #include "jspubtd.h"
 
 #include "ds/BitArray.h"
+#include "gc/Heap.h"
+#include "js/GCAPI.h"
 #include "js/HashTable.h"
+#include "js/HeapAPI.h"
+#include "js/Value.h"
+
+namespace JS {
+struct Zone;
+}
 
 namespace js {
 
 class ObjectElements;
+class HeapSlot;
 
 namespace gc {
+class Cell;
 class MinorCollectionTracer;
 } /* namespace gc */
 
 namespace jit {
 class CodeGenerator;
 class MacroAssembler;
 class ICStubCompiler;
 class BaselineCompiler;
@@ -123,17 +133,17 @@ class Nursery
      * The set of externally malloced slots potentially kept live by objects
      * stored in the nursery. Any external slots that do not belong to a
      * tenured thing at the end of a minor GC must be freed.
      */
     typedef HashSet<HeapSlot *, PointerHasher<HeapSlot *, 3>, SystemAllocPolicy> HugeSlotsSet;
     HugeSlotsSet hugeSlots;
 
     /* The marking bitmap for the fallback marker. */
-    const static size_t ThingAlignment = sizeof(Value);
+    const static size_t ThingAlignment = sizeof(JS::Value);
     const static size_t FallbackBitmapBits = NurserySize / ThingAlignment;
     BitArray<FallbackBitmapBits> fallbackBitmap;
 
 #ifdef DEBUG
     /*
      * In DEBUG builds, these bytes indicate the state of an unused segment of
      * nursery-allocated memory.
      */
@@ -198,17 +208,17 @@ class Nursery
     void *addressOfPosition() const { return (void*)&position_; }
 
     JSRuntime *runtime() const { return runtime_; }
 
     /* Allocates and registers external slots with the nursery. */
     HeapSlot *allocateHugeSlots(JSContext *cx, size_t nslots);
 
     /* Allocates a new GC thing from the tenured generation during minor GC. */
-    void *allocateFromTenured(Zone *zone, gc::AllocKind thingKind);
+    void *allocateFromTenured(JS::Zone *zone, gc::AllocKind thingKind);
 
     /*
      * Move the object at |src| in the Nursery to an already-allocated cell
      * |dst| in Tenured.
      */
     void collectToFixedPoint(gc::MinorCollectionTracer *trc);
     JS_ALWAYS_INLINE void traceObject(gc::MinorCollectionTracer *trc, JSObject *src);
     JS_ALWAYS_INLINE void markSlots(gc::MinorCollectionTracer *trc, HeapSlot *vp, uint32_t nslots);
--- a/js/src/gc/StoreBuffer.h
+++ b/js/src/gc/StoreBuffer.h
@@ -15,16 +15,17 @@
 
 #include "mozilla/DebugOnly.h"
 #include "mozilla/ReentrancyGuard.h"
 
 #include "jsalloc.h"
 
 #include "ds/LifoAlloc.h"
 #include "gc/Nursery.h"
+#include "js/Tracer.h"
 
 namespace js {
 namespace gc {
 
 class AccumulateEdgesTracer;
 
 /*
  * BufferableRef represents an abstract reference for use in the generational
@@ -246,50 +247,50 @@ class StoreBuffer
     };
 
     class ValueEdge
     {
         friend class StoreBuffer;
         friend class StoreBuffer::MonoTypeBuffer<ValueEdge>;
         friend class StoreBuffer::RelocatableMonoTypeBuffer<ValueEdge>;
 
-        Value *edge;
+        JS::Value *edge;
 
-        explicit ValueEdge(Value *v) : edge(v) {}
+        explicit ValueEdge(JS::Value *v) : edge(v) {}
         bool operator==(const ValueEdge &other) const { return edge == other.edge; }
         bool operator!=(const ValueEdge &other) const { return edge != other.edge; }
 
         void *deref() const { return edge->isGCThing() ? edge->toGCThing() : NULL; }
         void *location() const { return (void *)untagged().edge; }
 
         bool inRememberedSet(const Nursery &nursery) const {
             return !nursery.isInside(edge) && nursery.isInside(deref());
         }
 
         bool isNullEdge() const {
             return !deref();
         }
 
         void mark(JSTracer *trc);
 
-        ValueEdge tagged() const { return ValueEdge((Value *)(uintptr_t(edge) | 1)); }
-        ValueEdge untagged() const { return ValueEdge((Value *)(uintptr_t(edge) & ~1)); }
+        ValueEdge tagged() const { return ValueEdge((JS::Value *)(uintptr_t(edge) | 1)); }
+        ValueEdge untagged() const { return ValueEdge((JS::Value *)(uintptr_t(edge) & ~1)); }
         bool isTagged() const { return bool(uintptr_t(edge) & 1); }
     };
 
     struct SlotEdge
     {
         friend class StoreBuffer;
         friend class StoreBuffer::MonoTypeBuffer<SlotEdge>;
 
         JSObject *object;
         uint32_t offset;
-        HeapSlot::Kind kind;
+        int kind; // this is really just HeapSlot::Kind, but we can't see that type easily here
 
-        SlotEdge(JSObject *object, HeapSlot::Kind kind, uint32_t offset)
+        SlotEdge(JSObject *object, int kind, uint32_t offset)
           : object(object), offset(offset), kind(kind)
         {}
 
         bool operator==(const SlotEdge &other) const {
             return object == other.object && offset == other.offset && kind == other.kind;
         }
 
         bool operator!=(const SlotEdge &other) const {
@@ -378,53 +379,53 @@ class StoreBuffer
     bool isEnabled() { return enabled; }
 
     bool clear();
 
     /* Get the overflowed status. */
     bool isAboutToOverflow() const { return aboutToOverflow; }
 
     /* Insert a single edge into the buffer/remembered set. */
-    void putValue(Value *valuep) {
+    void putValue(JS::Value *valuep) {
         ValueEdge edge(valuep);
         if (!edge.inRememberedSet(nursery_))
             return;
         bufferVal.put(edge);
     }
     void putCell(Cell **cellp) {
         CellPtrEdge edge(cellp);
         if (!edge.inRememberedSet(nursery_))
             return;
         bufferCell.put(edge);
     }
-    void putSlot(JSObject *obj, HeapSlot::Kind kind, uint32_t slot, void *target) {
+    void putSlot(JSObject *obj, int kind, uint32_t slot, void *target) {
         SlotEdge edge(obj, kind, slot);
         /* This is manually inlined because slotLocation cannot be defined here. */
         if (nursery_.isInside(obj) || !nursery_.isInside(target))
             return;
         bufferSlot.put(edge);
     }
     void putWholeCell(Cell *cell) {
         bufferWholeCell.put(WholeCellEdges(cell));
     }
 
     /* Insert or update a single edge in the Relocatable buffer. */
-    void putRelocatableValue(Value *valuep) {
+    void putRelocatableValue(JS::Value *valuep) {
         ValueEdge edge(valuep);
         if (!edge.inRememberedSet(nursery_))
             return;
         bufferRelocVal.put(edge);
     }
     void putRelocatableCell(Cell **cellp) {
         CellPtrEdge edge(cellp);
         if (!edge.inRememberedSet(nursery_))
             return;
         bufferRelocCell.put(edge);
     }
-    void removeRelocatableValue(Value *valuep) {
+    void removeRelocatableValue(JS::Value *valuep) {
         ValueEdge edge(valuep);
         if (!edge.inRememberedSet(nursery_))
             return;
         bufferRelocVal.unput(edge);
     }
     void removeRelocatableCell(Cell **cellp) {
         CellPtrEdge edge(cellp);
         if (!edge.inRememberedSet(nursery_))
new file mode 100644
--- /dev/null
+++ b/js/src/gc/Tracer.cpp
@@ -0,0 +1,258 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * 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 "js/Tracer.h"
+
+#include "jsapi.h"
+#include "jsfun.h"
+#include "jsgc.h"
+#include "jsprf.h"
+#include "jsscript.h"
+#include "NamespaceImports.h"
+
+#include "gc/Marking.h"
+
+using namespace js;
+using namespace js::gc;
+
+JS_PUBLIC_API(void)
+JS_CallValueTracer(JSTracer *trc, Value *valuep, const char *name)
+{
+    MarkValueUnbarriered(trc, valuep, name);
+}
+
+JS_PUBLIC_API(void)
+JS_CallIdTracer(JSTracer *trc, jsid *idp, const char *name)
+{
+    MarkIdUnbarriered(trc, idp, name);
+}
+
+JS_PUBLIC_API(void)
+JS_CallObjectTracer(JSTracer *trc, JSObject **objp, const char *name)
+{
+    MarkObjectUnbarriered(trc, objp, name);
+}
+
+JS_PUBLIC_API(void)
+JS_CallStringTracer(JSTracer *trc, JSString **strp, const char *name)
+{
+    MarkStringUnbarriered(trc, strp, name);
+}
+
+JS_PUBLIC_API(void)
+JS_CallScriptTracer(JSTracer *trc, JSScript **scriptp, const char *name)
+{
+    MarkScriptUnbarriered(trc, scriptp, name);
+}
+
+JS_PUBLIC_API(void)
+JS_CallHeapValueTracer(JSTracer *trc, JS::Heap<JS::Value> *valuep, const char *name)
+{
+    MarkValueUnbarriered(trc, valuep->unsafeGet(), name);
+}
+
+JS_PUBLIC_API(void)
+JS_CallHeapIdTracer(JSTracer *trc, JS::Heap<jsid> *idp, const char *name)
+{
+    MarkIdUnbarriered(trc, idp->unsafeGet(), name);
+}
+
+JS_PUBLIC_API(void)
+JS_CallHeapObjectTracer(JSTracer *trc, JS::Heap<JSObject *> *objp, const char *name)
+{
+    MarkObjectUnbarriered(trc, objp->unsafeGet(), name);
+}
+
+JS_PUBLIC_API(void)
+JS_CallHeapStringTracer(JSTracer *trc, JS::Heap<JSString *> *strp, const char *name)
+{
+    MarkStringUnbarriered(trc, strp->unsafeGet(), name);
+}
+
+JS_PUBLIC_API(void)
+JS_CallHeapScriptTracer(JSTracer *trc, JS::Heap<JSScript *> *scriptp, const char *name)
+{
+    MarkScriptUnbarriered(trc, scriptp->unsafeGet(), name);
+}
+
+JS_PUBLIC_API(void)
+JS_CallTenuredObjectTracer(JSTracer *trc, JS::TenuredHeap<JSObject *> *objp, const char *name)
+{
+    JSObject *obj = objp->getPtr();
+    if (!obj)
+        return;
+
+    JS_SET_TRACING_LOCATION(trc, (void*)objp);
+    MarkObjectUnbarriered(trc, &obj, name);
+
+    objp->setPtr(obj);
+}
+
+JS_PUBLIC_API(void)
+JS_TracerInit(JSTracer *trc, JSRuntime *rt, JSTraceCallback callback)
+{
+    InitTracer(trc, rt, callback);
+}
+
+JS_PUBLIC_API(void)
+JS_TraceChildren(JSTracer *trc, void *thing, JSGCTraceKind kind)
+{
+    js::TraceChildren(trc, thing, kind);
+}
+
+JS_PUBLIC_API(void)
+JS_TraceRuntime(JSTracer *trc)
+{
+    AssertHeapIsIdle(trc->runtime);
+    TraceRuntime(trc);
+}
+
+static size_t
+CountDecimalDigits(size_t num)
+{
+    size_t numDigits = 0;
+    do {
+        num /= 10;
+        numDigits++;
+    } while (num > 0);
+
+    return numDigits;
+}
+
+JS_PUBLIC_API(void)
+JS_GetTraceThingInfo(char *buf, size_t bufsize, JSTracer *trc, void *thing,
+                     JSGCTraceKind kind, bool details)
+{
+    const char *name = NULL; /* silence uninitialized warning */
+    size_t n;
+
+    if (bufsize == 0)
+        return;
+
+    switch (kind) {
+      case JSTRACE_OBJECT:
+      {
+        name = static_cast<JSObject *>(thing)->getClass()->name;
+        break;
+      }
+
+      case JSTRACE_STRING:
+        name = ((JSString *)thing)->isDependent()
+               ? "substring"
+               : "string";
+        break;
+
+      case JSTRACE_SCRIPT:
+        name = "script";
+        break;
+
+      case JSTRACE_LAZY_SCRIPT:
+        name = "lazyscript";
+        break;
+
+      case JSTRACE_IONCODE:
+        name = "ioncode";
+        break;
+
+      case JSTRACE_SHAPE:
+        name = "shape";
+        break;
+
+      case JSTRACE_BASE_SHAPE:
+        name = "base_shape";
+        break;
+
+      case JSTRACE_TYPE_OBJECT:
+        name = "type_object";
+        break;
+    }
+
+    n = strlen(name);
+    if (n > bufsize - 1)
+        n = bufsize - 1;
+    js_memcpy(buf, name, n + 1);
+    buf += n;
+    bufsize -= n;
+    *buf = '\0';
+
+    if (details && bufsize > 2) {
+        switch (kind) {
+          case JSTRACE_OBJECT:
+          {
+            JSObject *obj = (JSObject *)thing;
+            if (obj->is<JSFunction>()) {
+                JSFunction *fun = &obj->as<JSFunction>();
+                if (fun->displayAtom()) {
+                    *buf++ = ' ';
+                    bufsize--;
+                    PutEscapedString(buf, bufsize, fun->displayAtom(), 0);
+                }
+            } else if (obj->getClass()->flags & JSCLASS_HAS_PRIVATE) {
+                JS_snprintf(buf, bufsize, " %p", obj->getPrivate());
+            } else {
+                JS_snprintf(buf, bufsize, " <no private>");
+            }
+            break;
+          }
+
+          case JSTRACE_STRING:
+          {
+            *buf++ = ' ';
+            bufsize--;
+            JSString *str = (JSString *)thing;
+
+            if (str->isLinear()) {
+                bool willFit = str->length() + strlen("<length > ") +
+                               CountDecimalDigits(str->length()) < bufsize;
+
+                n = JS_snprintf(buf, bufsize, "<length %d%s> ",
+                                (int)str->length(),
+                                willFit ? "" : " (truncated)");
+                buf += n;
+                bufsize -= n;
+
+                PutEscapedString(buf, bufsize, &str->asLinear(), 0);
+            }
+            else
+                JS_snprintf(buf, bufsize, "<rope: length %d>", (int)str->length());
+            break;
+          }
+
+          case JSTRACE_SCRIPT:
+          {
+            JSScript *script = static_cast<JSScript *>(thing);
+            JS_snprintf(buf, bufsize, " %s:%u", script->filename(), unsigned(script->lineno));
+            break;
+          }
+
+          case JSTRACE_LAZY_SCRIPT:
+          case JSTRACE_IONCODE:
+          case JSTRACE_SHAPE:
+          case JSTRACE_BASE_SHAPE:
+          case JSTRACE_TYPE_OBJECT:
+            break;
+        }
+    }
+    buf[bufsize - 1] = '\0';
+}
+
+extern JS_PUBLIC_API(const char *)
+JS_GetTraceEdgeName(JSTracer *trc, char *buffer, int bufferSize)
+{
+    if (trc->debugPrinter) {
+        trc->debugPrinter(trc, buffer, bufferSize);
+        return buffer;
+    }
+    if (trc->debugPrintIndex != (size_t) - 1) {
+        JS_snprintf(buffer, bufferSize, "%s[%lu]",
+                    (const char *)trc->debugPrintArg,
+                    trc->debugPrintIndex);
+        return buffer;
+    }
+    return (const char*)trc->debugPrintArg;
+}
+
+
--- a/js/src/gc/Zone.cpp
+++ b/js/src/gc/Zone.cpp
@@ -19,17 +19,17 @@
 #include "jsgcinlines.h"
 
 #include "vm/ObjectImpl-inl.h"
 
 using namespace js;
 using namespace js::gc;
 
 JS::Zone::Zone(JSRuntime *rt)
-  : runtime_(rt),
+  : JS::shadow::Zone(rt, &rt->gcMarker),
     allocator(this),
     hold(false),
     ionUsingBarriers_(false),
     active(false),
     gcScheduled(false),
     gcState(NoGC),
     gcPreserveCode(false),
     gcBytes(0),
@@ -229,8 +229,16 @@ Zone::discardJitCode(FreeOp *fop, bool d
             if (comp->ionCompartment())
                 comp->ionCompartment()->optimizedStubSpace()->free();
 
             comp->types.sweepCompilerOutputs(fop, discardConstraints);
         }
     }
 #endif
 }
+
+JS::Zone *
+js::ZoneOfObject(const JSObject &obj)
+{
+    return obj.zone();
+}
+
+
--- a/js/src/gc/Zone.h
+++ b/js/src/gc/Zone.h
@@ -87,53 +87,36 @@ namespace JS {
  * shapes within it are alive.
  *
  * We always guarantee that a zone has at least one live compartment by refusing
  * to delete the last compartment in a live zone. (This could happen, for
  * example, if the conservative scanner marks a string in an otherwise dead
  * zone.)
  */
 
-struct Zone : private JS::shadow::Zone,
+struct Zone : public JS::shadow::Zone,
               public js::gc::GraphNodeBase<JS::Zone>,
               public js::MallocProvider<JS::Zone>
 {
   private:
-    JSRuntime                    *runtime_;
-
     friend bool js::CurrentThreadCanAccessZone(Zone *zone);
 
   public:
     js::Allocator                allocator;
 
     js::CompartmentVector        compartments;
 
     bool                         hold;
 
   private:
     bool                         ionUsingBarriers_;
 
   public:
     bool                         active;  // GC flag, whether there are active frames
 
-    JSRuntime *runtimeFromMainThread() const {
-        JS_ASSERT(CurrentThreadCanAccessRuntime(runtime_));
-        return runtime_;
-    }
-
-    // Note: Unrestricted access to the zone's runtime from an arbitrary
-    // thread can easily lead to races. Use this method very carefully.
-    JSRuntime *runtimeFromAnyThread() const {
-        return runtime_;
-    }
-
-    bool needsBarrier() const {
-        return needsBarrier_;
-    }
-
     bool compileBarriers(bool needsBarrier) const {
         return needsBarrier || runtimeFromMainThread()->gcZeal() == js::gc::ZealVerifierPreValue;
     }
 
     bool compileBarriers() const {
         return compileBarriers(needsBarrier());
     }
 
@@ -143,21 +126,16 @@ struct Zone : private JS::shadow::Zone,
     };
 
     void setNeedsBarrier(bool needs, ShouldUpdateIon updateIon);
 
     static size_t OffsetOfNeedsBarrier() {
         return offsetof(Zone, needsBarrier_);
     }
 
-    js::GCMarker *barrierTracer() {
-        JS_ASSERT(needsBarrier_);
-        return &runtimeFromMainThread()->gcMarker;
-    }
-
   public:
     enum CompartmentGCState {
         NoGC,
         Mark,
         MarkGray,
         Sweep,
         Finished
     };
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -618,17 +618,17 @@ CodeGenerator::visitIntToString(LIntToSt
 
     masm.bind(ool->rejoin());
     return true;
 }
 
 typedef JSString *(*DoubleToStringFn)(ThreadSafeContext *, double);
 typedef ParallelResult (*DoubleToStringParFn)(ForkJoinSlice *, double, MutableHandleString);
 static const VMFunctionsModal DoubleToStringInfo = VMFunctionsModal(
-    FunctionInfo<DoubleToStringFn>(js_NumberToString<CanGC>),
+    FunctionInfo<DoubleToStringFn>(NumberToString<CanGC>),
     FunctionInfo<DoubleToStringParFn>(DoubleToStringPar));
 
 bool
 CodeGenerator::visitDoubleToString(LDoubleToString *lir)
 {
     FloatRegister input = ToFloatRegister(lir->input());
     Register temp = ToRegister(lir->tempInt());
     Register output = ToRegister(lir->output());
--- a/js/src/jit/ParallelFunctions.cpp
+++ b/js/src/jit/ParallelFunctions.cpp
@@ -206,17 +206,17 @@ jit::IntToStringPar(ForkJoinSlice *slice
         return TP_RETRY_SEQUENTIALLY;
     out.set(str);
     return TP_SUCCESS;
 }
 
 ParallelResult
 jit::DoubleToStringPar(ForkJoinSlice *slice, double d, MutableHandleString out)
 {
-    JSString *str = js_NumberToString<NoGC>(slice, d);
+    JSString *str = NumberToString<NoGC>(slice, d);
     if (!str)
         return TP_RETRY_SEQUENTIALLY;
     out.set(str);
     return TP_SUCCESS;
 }
 
 ParallelResult
 jit::StringToNumberPar(ForkJoinSlice *slice, JSString *str, double *out)
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -1896,253 +1896,16 @@ JS_RemoveExtraGCRootsTracer(JSRuntime *r
         JSRuntime::ExtraTracer *e = &rt->gcBlackRootTracers[i];
         if (e->op == traceOp && e->data == data) {
             rt->gcBlackRootTracers.erase(e);
             break;
         }
     }
 }
 
-JS_PUBLIC_API(void)
-JS_CallValueTracer(JSTracer *trc, Value *valuep, const char *name)
-{
-    MarkValueUnbarriered(trc, valuep, name);
-}
-
-JS_PUBLIC_API(void)
-JS_CallIdTracer(JSTracer *trc, jsid *idp, const char *name)
-{
-    MarkIdUnbarriered(trc, idp, name);
-}
-
-JS_PUBLIC_API(void)
-JS_CallObjectTracer(JSTracer *trc, JSObject **objp, const char *name)
-{
-    MarkObjectUnbarriered(trc, objp, name);
-}
-
-JS_PUBLIC_API(void)
-JS_CallTenuredObjectTracer(JSTracer *trc, JS::TenuredHeap<JSObject *> *objp, const char *name)
-{
-    JSObject *obj = objp->getPtr();
-    if (!obj)
-        return;
-
-    JS_SET_TRACING_LOCATION(trc, (void*)objp);
-    MarkObjectUnbarriered(trc, &obj, name);
-
-    objp->setPtr(obj);
-}
-
-JS_PUBLIC_API(void)
-JS_CallStringTracer(JSTracer *trc, JSString **strp, const char *name)
-{
-    MarkStringUnbarriered(trc, strp, name);
-}
-
-JS_PUBLIC_API(void)
-JS_CallScriptTracer(JSTracer *trc, JSScript **scriptp, const char *name)
-{
-    MarkScriptUnbarriered(trc, scriptp, name);
-}
-
-JS_PUBLIC_API(void)
-JS_CallHeapValueTracer(JSTracer *trc, JS::Heap<JS::Value> *valuep, const char *name)
-{
-    MarkValueUnbarriered(trc, valuep->unsafeGet(), name);
-}
-
-JS_PUBLIC_API(void)
-JS_CallHeapIdTracer(JSTracer *trc, JS::Heap<jsid> *idp, const char *name)
-{
-    MarkIdUnbarriered(trc, idp->unsafeGet(), name);
-}
-
-JS_PUBLIC_API(void)
-JS_CallHeapObjectTracer(JSTracer *trc, JS::Heap<JSObject *> *objp, const char *name)
-{
-    MarkObjectUnbarriered(trc, objp->unsafeGet(), name);
-}
-
-JS_PUBLIC_API(void)
-JS_CallHeapStringTracer(JSTracer *trc, JS::Heap<JSString *> *strp, const char *name)
-{
-    MarkStringUnbarriered(trc, strp->unsafeGet(), name);
-}
-
-JS_PUBLIC_API(void)
-JS_CallHeapScriptTracer(JSTracer *trc, JS::Heap<JSScript *> *scriptp, const char *name)
-{
-    MarkScriptUnbarriered(trc, scriptp->unsafeGet(), name);
-}
-
-JS_PUBLIC_API(void)
-JS_TracerInit(JSTracer *trc, JSRuntime *rt, JSTraceCallback callback)
-{
-    InitTracer(trc, rt, callback);
-}
-
-JS_PUBLIC_API(void)
-JS_TraceRuntime(JSTracer *trc)
-{
-    AssertHeapIsIdle(trc->runtime);
-    TraceRuntime(trc);
-}
-
-JS_PUBLIC_API(void)
-JS_TraceChildren(JSTracer *trc, void *thing, JSGCTraceKind kind)
-{
-    js::TraceChildren(trc, thing, kind);
-}
-
-static size_t
-CountDecimalDigits(size_t num)
-{
-    size_t numDigits = 0;
-    do {
-        num /= 10;
-        numDigits++;
-    } while (num > 0);
-
-    return numDigits;
-}
-
-JS_PUBLIC_API(void)
-JS_GetTraceThingInfo(char *buf, size_t bufsize, JSTracer *trc, void *thing,
-                     JSGCTraceKind kind, bool details)
-{
-    const char *name = NULL; /* silence uninitialized warning */
-    size_t n;
-
-    if (bufsize == 0)
-        return;
-
-    switch (kind) {
-      case JSTRACE_OBJECT:
-      {
-        name = static_cast<JSObject *>(thing)->getClass()->name;
-        break;
-      }
-
-      case JSTRACE_STRING:
-        name = ((JSString *)thing)->isDependent()
-               ? "substring"
-               : "string";
-        break;
-
-      case JSTRACE_SCRIPT:
-        name = "script";
-        break;
-
-      case JSTRACE_LAZY_SCRIPT:
-        name = "lazyscript";
-        break;
-
-      case JSTRACE_IONCODE:
-        name = "ioncode";
-        break;
-
-      case JSTRACE_SHAPE:
-        name = "shape";
-        break;
-
-      case JSTRACE_BASE_SHAPE:
-        name = "base_shape";
-        break;
-
-      case JSTRACE_TYPE_OBJECT:
-        name = "type_object";
-        break;
-    }
-
-    n = strlen(name);
-    if (n > bufsize - 1)
-        n = bufsize - 1;
-    js_memcpy(buf, name, n + 1);
-    buf += n;
-    bufsize -= n;
-    *buf = '\0';
-
-    if (details && bufsize > 2) {
-        switch (kind) {
-          case JSTRACE_OBJECT:
-          {
-            JSObject *obj = (JSObject *)thing;
-            if (obj->is<JSFunction>()) {
-                JSFunction *fun = &obj->as<JSFunction>();
-                if (fun->displayAtom()) {
-                    *buf++ = ' ';
-                    bufsize--;
-                    PutEscapedString(buf, bufsize, fun->displayAtom(), 0);
-                }
-            } else if (obj->getClass()->flags & JSCLASS_HAS_PRIVATE) {
-                JS_snprintf(buf, bufsize, " %p", obj->getPrivate());
-            } else {
-                JS_snprintf(buf, bufsize, " <no private>");
-            }
-            break;
-          }
-
-          case JSTRACE_STRING:
-          {
-            *buf++ = ' ';
-            bufsize--;
-            JSString *str = (JSString *)thing;
-
-            if (str->isLinear()) {
-                bool willFit = str->length() + strlen("<length > ") +
-                               CountDecimalDigits(str->length()) < bufsize;
-
-                n = JS_snprintf(buf, bufsize, "<length %d%s> ",
-                                (int)str->length(),
-                                willFit ? "" : " (truncated)");
-                buf += n;
-                bufsize -= n;
-
-                PutEscapedString(buf, bufsize, &str->asLinear(), 0);
-            }
-            else
-                JS_snprintf(buf, bufsize, "<rope: length %d>", (int)str->length());
-            break;
-          }
-
-          case JSTRACE_SCRIPT:
-          {
-            JSScript *script = static_cast<JSScript *>(thing);
-            JS_snprintf(buf, bufsize, " %s:%u", script->filename(), unsigned(script->lineno));
-            break;
-          }
-
-          case JSTRACE_LAZY_SCRIPT:
-          case JSTRACE_IONCODE:
-          case JSTRACE_SHAPE:
-          case JSTRACE_BASE_SHAPE:
-          case JSTRACE_TYPE_OBJECT:
-            break;
-        }
-    }
-    buf[bufsize - 1] = '\0';
-}
-
-extern JS_PUBLIC_API(const char *)
-JS_GetTraceEdgeName(JSTracer *trc, char *buffer, int bufferSize)
-{
-    if (trc->debugPrinter) {
-        trc->debugPrinter(trc, buffer, bufferSize);
-        return buffer;
-    }
-    if (trc->debugPrintIndex != (size_t) - 1) {
-        JS_snprintf(buffer, bufferSize, "%s[%lu]",
-                    (const char *)trc->debugPrintArg,
-                    trc->debugPrintIndex);
-        return buffer;
-    }
-    return (const char*)trc->debugPrintArg;
-}
-
 #ifdef DEBUG
 
 typedef struct JSHeapDumpNode JSHeapDumpNode;
 
 struct JSHeapDumpNode {
     void            *thing;
     JSGCTraceKind   kind;
     JSHeapDumpNode  *next;          /* next sibling */
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -27,16 +27,18 @@
 #include "js/Id.h"
 #include "js/RootingAPI.h"
 #include "js/Utility.h"
 #include "js/Value.h"
 #include "js/Vector.h"
 
 /************************************************************************/
 
+struct JSTracer;
+
 namespace JS {
 
 class Latin1CharsZ;
 class TwoByteChars;
 
 typedef mozilla::RangedPtr<const jschar> CharPtr;
 
 class StableCharPtr : public CharPtr {
@@ -1985,197 +1987,16 @@ JSVAL_TO_TRACEABLE(jsval v)
 
 static JS_ALWAYS_INLINE JSGCTraceKind
 JSVAL_TRACE_KIND(jsval v)
 {
     JS_ASSERT(JSVAL_IS_GCTHING(v));
     return (JSGCTraceKind) JSVAL_TRACE_KIND_IMPL(JSVAL_TO_IMPL(v));
 }
 
-/*
- * Tracer callback, called for each traceable thing directly referenced by a
- * particular object or runtime structure. It is the callback responsibility
- * to ensure the traversal of the full object graph via calling eventually
- * JS_TraceChildren on the passed thing. In this case the callback must be
- * prepared to deal with cycles in the traversal graph.
- *
- * kind argument is one of JSTRACE_OBJECT, JSTRACE_STRING or a tag denoting
- * internal implementation-specific traversal kind. In the latter case the only
- * operations on thing that the callback can do is to call JS_TraceChildren or
- * JS_GetTraceThingInfo.
- *
- * If eagerlyTraceWeakMaps is true, when we trace a WeakMap visit all
- * of its mappings.  This should be used in cases where the tracer
- * wants to use the existing liveness of entries.
- */
-typedef void
-(* JSTraceCallback)(JSTracer *trc, void **thingp, JSGCTraceKind kind);
-
-enum WeakMapTraceKind {
-    DoNotTraceWeakMaps = 0,
-    TraceWeakMapValues = 1,
-    TraceWeakMapKeysValues = 2
-};
-
-struct JSTracer {
-    JSRuntime           *runtime;
-    JSTraceCallback     callback;
-    JSTraceNamePrinter  debugPrinter;
-    const void          *debugPrintArg;
-    size_t              debugPrintIndex;
-    WeakMapTraceKind    eagerlyTraceWeakMaps;
-#ifdef JS_GC_ZEAL
-    void                *realLocation;
-#endif
-};
-
-/*
- * Set debugging information about a reference to a traceable thing to prepare
- * for the following call to JS_CallTracer.
- *
- * When printer is null, arg must be const char * or char * C string naming
- * the reference and index must be either (size_t)-1 indicating that the name
- * alone describes the reference or it must be an index into some array vector
- * that stores the reference.
- *
- * When printer callback is not null, the arg and index arguments are
- * available to the callback as debugPrintArg and debugPrintIndex fields
- * of JSTracer.
- *
- * The storage for name or callback's arguments needs to live only until
- * the following call to JS_CallTracer returns.
- */
-# define JS_SET_TRACING_DETAILS(trc, printer, arg, index)                     \
-    JS_BEGIN_MACRO                                                            \
-        (trc)->debugPrinter = (printer);                                      \
-        (trc)->debugPrintArg = (arg);                                         \
-        (trc)->debugPrintIndex = (index);                                     \
-    JS_END_MACRO
-
-/*
- * Sets the real location for a marked reference, when passing the address
- * directly is not feasable.
- *
- * FIXME: This is currently overcomplicated by our need to nest calls for Values
- * stored as keys in hash tables, but will get simplified once we can rekey
- * in-place.
- */
-#ifdef JS_GC_ZEAL
-# define JS_SET_TRACING_LOCATION(trc, location)                               \
-    JS_BEGIN_MACRO                                                            \
-        if (!(trc)->realLocation || !(location))                              \
-            (trc)->realLocation = (location);                                 \
-    JS_END_MACRO
-# define JS_UNSET_TRACING_LOCATION(trc)                                       \
-    JS_BEGIN_MACRO                                                            \
-        (trc)->realLocation = NULL;                                           \
-    JS_END_MACRO
-#else
-# define JS_SET_TRACING_LOCATION(trc, location)                               \
-    JS_BEGIN_MACRO                                                            \
-    JS_END_MACRO
-# define JS_UNSET_TRACING_LOCATION(trc)                                       \
-    JS_BEGIN_MACRO                                                            \
-    JS_END_MACRO
-#endif
-
-
-/*
- * Convenience macro to describe the argument of JS_CallTracer using C string
- * and index.
- */
-# define JS_SET_TRACING_INDEX(trc, name, index)                               \
-    JS_SET_TRACING_DETAILS(trc, NULL, name, index)
-
-/*
- * Convenience macro to describe the argument of JS_CallTracer using C string.
- */
-# define JS_SET_TRACING_NAME(trc, name)                                       \
-    JS_SET_TRACING_DETAILS(trc, NULL, name, (size_t)-1)
-
-/*
- * The JS_Call*Tracer family of functions traces the given GC thing reference.
- * This performs the tracing action configured on the given JSTracer:
- * typically calling the JSTracer::callback or marking the thing as live.
- *
- * The argument to JS_Call*Tracer is an in-out param: when the function
- * returns, the garbage collector might have moved the GC thing. In this case,
- * the reference passed to JS_Call*Tracer will be updated to the object's new
- * location. Callers of this method are responsible for updating any state
- * that is dependent on the object's address. For example, if the object's
- * address is used as a key in a hashtable, then the object must be removed
- * and re-inserted with the correct hash.
- */
-extern JS_PUBLIC_API(void)
-JS_CallValueTracer(JSTracer *trc, JS::Value *valuep, const char *name);
-
-extern JS_PUBLIC_API(void)
-JS_CallIdTracer(JSTracer *trc, jsid *idp, const char *name);
-
-extern JS_PUBLIC_API(void)
-JS_CallObjectTracer(JSTracer *trc, JSObject **objp, const char *name);
-
-extern JS_PUBLIC_API(void)
-JS_CallStringTracer(JSTracer *trc, JSString **strp, const char *name);
-
-extern JS_PUBLIC_API(void)
-JS_CallScriptTracer(JSTracer *trc, JSScript **scriptp, const char *name);
-
-extern JS_PUBLIC_API(void)
-JS_CallHeapValueTracer(JSTracer *trc, JS::Heap<JS::Value> *valuep, const char *name);
-
-extern JS_PUBLIC_API(void)
-JS_CallHeapIdTracer(JSTracer *trc, JS::Heap<jsid> *idp, const char *name);
-
-extern JS_PUBLIC_API(void)
-JS_CallHeapObjectTracer(JSTracer *trc, JS::Heap<JSObject *> *objp, const char *name);
-
-extern JS_PUBLIC_API(void)
-JS_CallHeapStringTracer(JSTracer *trc, JS::Heap<JSString *> *strp, const char *name);
-
-extern JS_PUBLIC_API(void)
-JS_CallHeapScriptTracer(JSTracer *trc, JS::Heap<JSScript *> *scriptp, const char *name);
-
-template <typename HashSetEnum>
-inline void
-JS_CallHashSetObjectTracer(JSTracer *trc, HashSetEnum &e, JSObject *const &key, const char *name)
-{
-    JSObject *updated = key;
-    JS_SET_TRACING_LOCATION(trc, reinterpret_cast<void *>(&const_cast<JSObject *&>(key)));
-    JS_CallObjectTracer(trc, &updated, name);
-    if (updated != key)
-        e.rekeyFront(key, updated);
-}
-
-/*
- * Trace an object that is known to always be tenured.  No post barriers are
- * required in this case.
- */
-extern JS_PUBLIC_API(void)
-JS_CallTenuredObjectTracer(JSTracer *trc, JS::TenuredHeap<JSObject *> *objp, const char *name);
-
-/*
- * API for JSTraceCallback implementations.
- */
-extern JS_PUBLIC_API(void)
-JS_TracerInit(JSTracer *trc, JSRuntime *rt, JSTraceCallback callback);
-
-extern JS_PUBLIC_API(void)
-JS_TraceChildren(JSTracer *trc, void *thing, JSGCTraceKind kind);
-
-extern JS_PUBLIC_API(void)
-JS_TraceRuntime(JSTracer *trc);
-
-extern JS_PUBLIC_API(void)
-JS_GetTraceThingInfo(char *buf, size_t bufsize, JSTracer *trc,
-                     void *thing, JSGCTraceKind kind, bool includeDetails);
-
-extern JS_PUBLIC_API(const char *)
-JS_GetTraceEdgeName(JSTracer *trc, char *buffer, int bufferSize);
-
 #ifdef DEBUG
 
 /*
  * DEBUG-only method to dump the object graph of heap-allocated things.
  *
  * fp:              file for the dump output.
  * start:           when non-null, dump only things reachable from start
  *                  thing. Otherwise dump all things reachable from the
--- a/js/src/jsatom.cpp
+++ b/js/src/jsatom.cpp
@@ -19,16 +19,17 @@
 #include "jsstr.h"
 #include "jstypes.h"
 
 #include "gc/Marking.h"
 #include "vm/Xdr.h"
 
 #include "jscntxtinlines.h"
 #include "jscompartmentinlines.h"
+#include "jsobjinlines.h"
 
 #include "vm/String-inl.h"
 
 using namespace js;
 using namespace js::gc;
 
 using mozilla::ArrayEnd;
 using mozilla::ArrayLength;
@@ -220,27 +221,22 @@ AtomIsInterned(JSContext *cx, JSAtom *at
 
     AtomSet::Ptr p = cx->runtime()->atoms().lookup(atom);
     if (!p)
         return false;
 
     return p->isTagged();
 }
 
-enum OwnCharsBehavior
-{
-    CopyChars, /* in other words, do not take ownership */
-    TakeCharOwnership
-};
-
 /*
  * When the jschars reside in a freshly allocated buffer the memory can be used
  * as a new JSAtom's storage without copying. The contract is that the caller no
  * longer owns the memory and this method is responsible for freeing the memory.
  */
+template <AllowGC allowGC>
 JS_ALWAYS_INLINE
 static JSAtom *
 AtomizeAndTakeOwnership(ExclusiveContext *cx, jschar *tbchars, size_t length, InternBehavior ib)
 {
     JS_ASSERT(tbchars[length] == 0);
 
     if (JSAtom *s = cx->staticStrings().lookup(tbchars, length)) {
         js_free(tbchars);
@@ -262,17 +258,17 @@ AtomizeAndTakeOwnership(ExclusiveContext
         JSAtom *atom = p->asPtr();
         p->setTagged(bool(ib));
         js_free(tbchars);
         return atom;
     }
 
     AutoCompartment ac(cx, cx->atomsCompartment());
 
-    JSFlatString *flat = js_NewString<CanGC>(cx, tbchars, length);
+    JSFlatString *flat = js_NewString<allowGC>(cx, tbchars, length);
     if (!flat) {
         js_free(tbchars);
         return NULL;
     }
 
     JSAtom *atom = flat->morphAtomizedStringIntoAtom();
 
     if (!atoms.relookupOrAdd(p, AtomHasher::Lookup(tbchars, length),
@@ -369,18 +365,19 @@ js::AtomizeString(ExclusiveContext *cx, 
 }
 
 template JSAtom *
 js::AtomizeString<CanGC>(ExclusiveContext *cx, JSString *str, InternBehavior ib);
 
 template JSAtom *
 js::AtomizeString<NoGC>(ExclusiveContext *cx, JSString *str, InternBehavior ib);
 
+template <AllowGC allowGC>
 JSAtom *
-js::Atomize(ExclusiveContext *cx, const char *bytes, size_t length, InternBehavior ib)
+js::AtomizeMaybeGC(ExclusiveContext *cx, const char *bytes, size_t length, InternBehavior ib)
 {
     CHECK_REQUEST(cx);
 
     if (!JSString::validateLength(cx, length))
         return NULL;
 
     static const unsigned ATOMIZE_BUF_MAX = 32;
     if (length < ATOMIZE_BUF_MAX) {
@@ -393,23 +390,37 @@ js::Atomize(ExclusiveContext *cx, const 
          */
         jschar inflated[ATOMIZE_BUF_MAX];
         size_t inflatedLength = ATOMIZE_BUF_MAX - 1;
         if (!InflateStringToBuffer(cx->maybeJSContext(),
                                    bytes, length, inflated, &inflatedLength))
         {
             return NULL;
         }
-        return AtomizeAndCopyChars<CanGC>(cx, inflated, inflatedLength, ib);
+        return AtomizeAndCopyChars<allowGC>(cx, inflated, inflatedLength, ib);
     }
 
     jschar *tbcharsZ = InflateString(cx, bytes, &length);
     if (!tbcharsZ)
         return NULL;
-    return AtomizeAndTakeOwnership(cx, tbcharsZ, length, ib);
+    return AtomizeAndTakeOwnership<allowGC>(cx, tbcharsZ, length, ib);
+}
+
+template JSAtom *
+js::AtomizeMaybeGC<CanGC>(ExclusiveContext *cx, const char *bytes, size_t length,
+                          InternBehavior ib);
+
+template JSAtom *
+js::AtomizeMaybeGC<NoGC>(ExclusiveContext *cx, const char *bytes, size_t length,
+                         InternBehavior ib);
+
+JSAtom *
+js::Atomize(ExclusiveContext *cx, const char *bytes, size_t length, InternBehavior ib)
+{
+    return AtomizeMaybeGC<CanGC>(cx, bytes, length, ib);
 }
 
 template <AllowGC allowGC>
 JSAtom *
 js::AtomizeChars(ExclusiveContext *cx, const jschar *chars, size_t length, InternBehavior ib)
 {
     CHECK_REQUEST(cx);
 
@@ -448,26 +459,50 @@ js::IndexToIdSlow(ExclusiveContext *cx, 
 
 template bool
 js::IndexToIdSlow<CanGC>(ExclusiveContext *cx, uint32_t index, MutableHandleId idp);
 
 template bool
 js::IndexToIdSlow<NoGC>(ExclusiveContext *cx, uint32_t index, FakeMutableHandle<jsid> idp);
 
 template <AllowGC allowGC>
+static JSAtom *
+ToAtomSlow(ExclusiveContext *cx, typename MaybeRooted<Value, allowGC>::HandleType arg)
+{
+    JS_ASSERT(!arg.isString());
+
+    Value v = arg;
+    if (!v.isPrimitive()) {
+        if (!cx->shouldBeJSContext() || !allowGC)
+            return NULL;
+        RootedValue v2(cx, v);
+        if (!ToPrimitive(cx->asJSContext(), JSTYPE_STRING, &v2))
+            return NULL;
+        v = v2;
+    }
+
+    if (v.isString())
+        return AtomizeString<allowGC>(cx, v.toString());
+    if (v.isInt32())
+        return Int32ToAtom<allowGC>(cx, v.toInt32());
+    if (v.isDouble())
+        return NumberToAtom<allowGC>(cx, v.toDouble());
+    if (v.isBoolean())
+        return v.toBoolean() ? cx->names().true_ : cx->names().false_;
+    if (v.isNull())
+        return cx->names().null;
+    return cx->names().undefined;
+}
+
+template <AllowGC allowGC>
 JSAtom *
 js::ToAtom(ExclusiveContext *cx, typename MaybeRooted<Value, allowGC>::HandleType v)
 {
-    if (!v.isString()) {
-        JSString *str = js::ToStringSlow<allowGC>(cx, v);
-        if (!str)
-            return NULL;
-        JS::Anchor<JSString *> anchor(str);
-        return AtomizeString<allowGC>(cx, str);
-    }
+    if (!v.isString())
+        return ToAtomSlow<allowGC>(cx, v);
 
     JSString *str = v.toString();
     if (str->isAtom())
         return &str->asAtom();
 
     JS::Anchor<JSString *> anchor(str);
     return AtomizeString<allowGC>(cx, str);
 }
--- a/js/src/jsatom.h
+++ b/js/src/jsatom.h
@@ -194,16 +194,21 @@ FinishCommonNames(JSRuntime *rt);
 
 /* N.B. must correspond to boolean tagging behavior. */
 enum InternBehavior
 {
     DoNotInternAtom = false,
     InternAtom = true
 };
 
+template <AllowGC allowGC>
+extern JSAtom *
+AtomizeMaybeGC(ExclusiveContext *cx, const char *bytes, size_t length,
+               js::InternBehavior ib = js::DoNotInternAtom);
+
 extern JSAtom *
 Atomize(ExclusiveContext *cx, const char *bytes, size_t length,
         js::InternBehavior ib = js::DoNotInternAtom);
 
 template <AllowGC allowGC>
 extern JSAtom *
 AtomizeChars(ExclusiveContext *cx, const jschar *chars, size_t length,
              js::InternBehavior ib = js::DoNotInternAtom);
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -199,34 +199,48 @@ class JSFunction : public JSObject
 
     // Can be called multiple times by the parser.
     void setIsExprClosure() {
         flags |= EXPR_CLOSURE;
     }
 
     JSAtom *atom() const { return hasGuessedAtom() ? NULL : atom_.get(); }
     js::PropertyName *name() const { return hasGuessedAtom() || !atom_ ? NULL : atom_->asPropertyName(); }
-    inline void initAtom(JSAtom *atom);
+    void initAtom(JSAtom *atom) { atom_.init(atom); }
     JSAtom *displayAtom() const { return atom_; }
 
-    inline void setGuessedAtom(JSAtom *atom);
+    void setGuessedAtom(JSAtom *atom) {
+        JS_ASSERT(atom_ == NULL);
+        JS_ASSERT(atom != NULL);
+        JS_ASSERT(!hasGuessedAtom());
+        atom_ = atom;
+        flags |= HAS_GUESSED_ATOM;
+    }
 
     /* uint16_t representation bounds number of call object dynamic slots. */
     enum { MAX_ARGS_AND_VARS = 2 * ((1U << 16) - 1) };
 
     /*
      * For an interpreted function, accessors for the initial scope object of
      * activations (stack frames) of the function.
      */
     JSObject *environment() const {
         JS_ASSERT(isInterpreted());
         return u.i.env_;
     }
-    inline void setEnvironment(JSObject *obj);
-    inline void initEnvironment(JSObject *obj);
+
+    void setEnvironment(JSObject *obj) {
+        JS_ASSERT(isInterpreted());
+        *(js::HeapPtrObject *)&u.i.env_ = obj;
+    }
+
+    void initEnvironment(JSObject *obj) {
+        JS_ASSERT(isInterpreted());
+        ((js::HeapPtrObject *)&u.i.env_)->init(obj);
+    }
 
     static inline size_t offsetOfEnvironment() { return offsetof(JSFunction, u.i.env_); }
     static inline size_t offsetOfAtom() { return offsetof(JSFunction, atom_); }
 
     static bool createScriptForLazilyInterpretedFunction(JSContext *cx, js::HandleFunction fun);
 
     // Function Scripts
     //
@@ -299,16 +313,17 @@ class JSFunction : public JSObject
     bool isGenerator() const { return generatorKind() != js::NotGenerator; }
 
     bool isLegacyGenerator() const { return generatorKind() == js::LegacyGenerator; }
 
     bool isStarGenerator() const { return generatorKind() == js::StarGenerator; }
 
     inline void setScript(JSScript *script_);
     inline void initScript(JSScript *script_);
+
     void initLazyScript(js::LazyScript *lazy) {
         JS_ASSERT(isInterpreted());
         flags &= ~INTERPRETED;
         flags |= INTERPRETED_LAZY;
         u.i.s.lazy_ = lazy;
     }
 
     JSNative native() const {
@@ -361,17 +376,23 @@ class JSFunction : public JSObject
 
     inline void trace(JSTracer *trc);
 
     /* Bound function accessors. */
 
     inline bool initBoundFunction(JSContext *cx, js::HandleValue thisArg,
                                   const js::Value *args, unsigned argslen);
 
-    inline JSObject *getBoundFunctionTarget() const;
+    JSObject *getBoundFunctionTarget() const {
+        JS_ASSERT(isBoundFunction());
+
+        /* Bound functions abuse |parent| to store their target function. */
+        return getParent();
+    }
+
     inline const js::Value &getBoundFunctionThis() const;
     inline const js::Value &getBoundFunctionArgument(unsigned which) const;
     inline size_t getBoundFunctionArgumentCount() const;
 
   private:
     inline js::FunctionExtended *toExtended();
     inline const js::FunctionExtended *toExtended() const;
 
@@ -484,16 +505,40 @@ JSFunction::toExtended()
 
 inline const js::FunctionExtended *
 JSFunction::toExtended() const
 {
     JS_ASSERT(isExtended());
     return static_cast<const js::FunctionExtended *>(this);
 }
 
+inline void
+JSFunction::initializeExtended()
+{
+    JS_ASSERT(isExtended());
+
+    JS_ASSERT(mozilla::ArrayLength(toExtended()->extendedSlots) == 2);
+    toExtended()->extendedSlots[0].init(js::UndefinedValue());
+    toExtended()->extendedSlots[1].init(js::UndefinedValue());
+}
+
+inline void
+JSFunction::initExtendedSlot(size_t which, const js::Value &val)
+{
+    JS_ASSERT(which < mozilla::ArrayLength(toExtended()->extendedSlots));
+    toExtended()->extendedSlots[which].init(val);
+}
+
+inline void
+JSFunction::setExtendedSlot(size_t which, const js::Value &val)
+{
+    JS_ASSERT(which < mozilla::ArrayLength(toExtended()->extendedSlots));
+    toExtended()->extendedSlots[which] = val;
+}
+
 inline const js::Value &
 JSFunction::getExtendedSlot(size_t which) const
 {
     JS_ASSERT(which < mozilla::ArrayLength(toExtended()->extendedSlots));
     return toExtended()->extendedSlots[which];
 }
 
 namespace js {
--- a/js/src/jsfuninlines.h
+++ b/js/src/jsfuninlines.h
@@ -6,70 +6,16 @@
 
 #ifndef jsfuninlines_h
 #define jsfuninlines_h
 
 #include "jsfun.h"
 
 #include "vm/ScopeObject.h"
 
-inline void
-JSFunction::initAtom(JSAtom *atom)
-{
-    atom_.init(atom);
-}
-
-inline void
-JSFunction::setGuessedAtom(JSAtom *atom)
-{
-    JS_ASSERT(atom_ == NULL);
-    JS_ASSERT(atom != NULL);
-    JS_ASSERT(!hasGuessedAtom());
-    atom_ = atom;
-    flags |= HAS_GUESSED_ATOM;
-}
-
-inline void
-JSFunction::setEnvironment(JSObject *obj)
-{
-    JS_ASSERT(isInterpreted());
-    *(js::HeapPtrObject *)&u.i.env_ = obj;
-}
-
-inline void
-JSFunction::initEnvironment(JSObject *obj)
-{
-    JS_ASSERT(isInterpreted());
-    ((js::HeapPtrObject *)&u.i.env_)->init(obj);
-}
-
-inline void
-JSFunction::initializeExtended()
-{
-    JS_ASSERT(isExtended());
-
-    JS_ASSERT(mozilla::ArrayLength(toExtended()->extendedSlots) == 2);
-    toExtended()->extendedSlots[0].init(js::UndefinedValue());
-    toExtended()->extendedSlots[1].init(js::UndefinedValue());
-}
-
-inline void
-JSFunction::initExtendedSlot(size_t which, const js::Value &val)
-{
-    JS_ASSERT(which < mozilla::ArrayLength(toExtended()->extendedSlots));
-    toExtended()->extendedSlots[which].init(val);
-}
-
-inline void
-JSFunction::setExtendedSlot(size_t which, const js::Value &val)
-{
-    JS_ASSERT(which < mozilla::ArrayLength(toExtended()->extendedSlots));
-    toExtended()->extendedSlots[which] = val;
-}
-
 namespace js {
 
 inline const char *
 GetFunctionNameBytes(JSContext *cx, JSFunction *fun, JSAutoByteString *bytes)
 {
     JSAtom *atom = fun->atom();
     if (atom)
         return bytes->encodeLatin1(cx, atom);
@@ -170,18 +116,9 @@ JSFunction::setScript(JSScript *script_)
 
 inline void
 JSFunction::initScript(JSScript *script_)
 {
     JS_ASSERT(isInterpreted());
     mutableScript().init(script_);
 }
 
-inline JSObject *
-JSFunction::getBoundFunctionTarget() const
-{
-    JS_ASSERT(isBoundFunction());
-
-    /* Bound functions abuse |parent| to store their target function. */
-    return getParent();
-}
-
 #endif /* jsfuninlines_h */
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -11,16 +11,17 @@
 
 #include "mozilla/DebugOnly.h"
 #include "mozilla/MemoryReporting.h"
 
 #include "jslock.h"
 #include "jsobj.h"
 
 #include "js/GCAPI.h"
+#include "js/Tracer.h"
 #include "js/Vector.h"
 
 class JSAtom;
 struct JSCompartment;
 class JSFlatString;
 class JSLinearString;
 
 namespace js {
--- a/js/src/jsnum.cpp
+++ b/js/src/jsnum.cpp
@@ -532,64 +532,123 @@ ToCStringBuf::ToCStringBuf() :dbuf(NULL)
     JS_STATIC_ASSERT(sbufSize >= DTOSTR_STANDARD_BUFFER_SIZE);
 }
 
 ToCStringBuf::~ToCStringBuf()
 {
     js_free(dbuf);
 }
 
+JS_ALWAYS_INLINE
+static JSFlatString *
+LookupDtoaCache(ThreadSafeContext *cx, double d)
+{
+    if (!cx->isExclusiveContext())
+        return NULL;
+
+    if (JSCompartment *comp = cx->asExclusiveContext()->compartment()) {
+        if (JSFlatString *str = comp->dtoaCache.lookup(10, d))
+            return str;
+    }
+
+    return NULL;
+}
+
+JS_ALWAYS_INLINE
+static void
+CacheNumber(ThreadSafeContext *cx, double d, JSFlatString *str)
+{
+    if (!cx->isExclusiveContext())
+        return;
+
+    if (JSCompartment *comp = cx->asExclusiveContext()->compartment())
+        comp->dtoaCache.cache(10, d, str);
+}
+
+JS_ALWAYS_INLINE
+static JSFlatString *
+LookupInt32ToString(ThreadSafeContext *cx, int32_t si)
+{
+    if (si >= 0 && StaticStrings::hasInt(si))
+        return cx->staticStrings().getInt(si);
+
+    return LookupDtoaCache(cx, si);
+}
+
+template <typename T>
+JS_ALWAYS_INLINE
+static T *
+BackfillInt32InBuffer(int32_t si, T *buffer, size_t size, size_t *length)
+{
+    uint32_t ui = mozilla::Abs(si);
+    JS_ASSERT_IF(si == INT32_MIN, ui == uint32_t(INT32_MAX) + 1);
+
+    RangedPtr<T> end(buffer + size - 1, buffer, size);
+    *end = '\0';
+    RangedPtr<T> start = BackfillIndexInCharBuffer(ui, end);
+    if (si < 0)
+        *--start = '-';
+
+    *length = end - start;
+    return start.get();
+}
+
 template <AllowGC allowGC>
 JSFlatString *
 js::Int32ToString(ThreadSafeContext *cx, int32_t si)
 {
-    uint32_t ui;
-    if (si >= 0) {
-        if (StaticStrings::hasInt(si))
-            return cx->staticStrings().getInt(si);
-        ui = si;
-    } else {
-        ui = uint32_t(-si);
-        JS_ASSERT_IF(si == INT32_MIN, ui == uint32_t(INT32_MAX) + 1);
-    }
-
-    JSCompartment *comp = cx->isExclusiveContext()
-                          ? cx->asExclusiveContext()->compartment()
-                          : NULL;
-    if (comp) {
-        if (JSFlatString *str = comp->dtoaCache.lookup(10, si))
-            return str;
-    }
+    if (JSFlatString *str = LookupInt32ToString(cx, si))
+        return str;
 
     JSShortString *str = js_NewGCShortString<allowGC>(cx);
     if (!str)
         return NULL;
 
     jschar buffer[JSShortString::MAX_SHORT_LENGTH + 1];
-    RangedPtr<jschar> end(buffer + JSShortString::MAX_SHORT_LENGTH,
-                          buffer, JSShortString::MAX_SHORT_LENGTH + 1);
-    *end = '\0';
-    RangedPtr<jschar> start = BackfillIndexInCharBuffer(ui, end);
-    if (si < 0)
-        *--start = '-';
+    size_t length;
+    jschar *start = BackfillInt32InBuffer(si, buffer,
+                                          JSShortString::MAX_SHORT_LENGTH + 1, &length);
 
-    jschar *dst = str->init(end - start);
-    PodCopy(dst, start.get(), end - start + 1);
+    PodCopy(str->init(length), start, length + 1);
 
-    if (comp)
-        comp->dtoaCache.cache(10, si, str);
+    CacheNumber(cx, si, str);
     return str;
 }
 
 template JSFlatString *
 js::Int32ToString<CanGC>(ThreadSafeContext *cx, int32_t si);
 
 template JSFlatString *
 js::Int32ToString<NoGC>(ThreadSafeContext *cx, int32_t si);
 
+template <AllowGC allowGC>
+JSAtom *
+js::Int32ToAtom(ExclusiveContext *cx, int32_t si)
+{
+    if (JSFlatString *str = LookupInt32ToString(cx, si))
+        return js::AtomizeString<allowGC>(cx, str);
+
+    char buffer[JSShortString::MAX_SHORT_LENGTH + 1];
+    size_t length;
+    char *start = BackfillInt32InBuffer(si, buffer, JSShortString::MAX_SHORT_LENGTH + 1, &length);
+
+    JSAtom *atom = AtomizeMaybeGC<allowGC>(cx, start, length);
+    if (!atom)
+        return NULL;
+
+    CacheNumber(cx, si, atom);
+    return atom;
+}
+
+template JSAtom *
+js::Int32ToAtom<CanGC>(ExclusiveContext *cx, int32_t si);
+
+template JSAtom *
+js::Int32ToAtom<NoGC>(ExclusiveContext *cx, int32_t si);
+
 /* Returns a non-NULL pointer to inside cbuf.  */
 static char *
 IntToCString(ToCStringBuf *cbuf, int i, int base = 10)
 {
     unsigned u = (i < 0) ? -i : i;
 
     RangedPtr<char> cp(cbuf->sbuf + cbuf->sbufSize - 1, cbuf->sbuf, cbuf->sbufSize);
     *cp = '\0';
@@ -1324,26 +1383,61 @@ js_NumberToStringWithBase(ThreadSafeCont
     if (comp)
         comp->dtoaCache.cache(base, d, s);
 
     return s;
 }
 
 template <AllowGC allowGC>
 JSString *
-js_NumberToString(ThreadSafeContext *cx, double d)
+js::NumberToString(ThreadSafeContext *cx, double d)
 {
     return js_NumberToStringWithBase<allowGC>(cx, d, 10);
 }
 
 template JSString *
-js_NumberToString<CanGC>(ThreadSafeContext *cx, double d);
+js::NumberToString<CanGC>(ThreadSafeContext *cx, double d);
 
 template JSString *
-js_NumberToString<NoGC>(ThreadSafeContext *cx, double d);
+js::NumberToString<NoGC>(ThreadSafeContext *cx, double d);
+
+template <AllowGC allowGC>
+JSAtom *
+js::NumberToAtom(ExclusiveContext *cx, double d)
+{
+    int32_t si;
+    if (mozilla::DoubleIsInt32(d, &si))
+        return Int32ToAtom<allowGC>(cx, si);
+
+    if (JSFlatString *str = LookupDtoaCache(cx, d))
+        return AtomizeString<allowGC>(cx, str);
+
+    ToCStringBuf cbuf;
+    char *numStr = FracNumberToCString(cx, &cbuf, d);
+    if (!numStr) {
+        js_ReportOutOfMemory(cx);
+        return NULL;
+    }
+    JS_ASSERT(!cbuf.dbuf && numStr >= cbuf.sbuf && numStr < cbuf.sbuf + cbuf.sbufSize);
+
+    size_t length = strlen(numStr);
+    JSAtom *atom = AtomizeMaybeGC<allowGC>(cx, numStr, length);
+    if (!atom)
+        return NULL;
+
+    CacheNumber(cx, d, atom);
+
+    return atom;
+}
+
+template JSAtom *
+js::NumberToAtom<CanGC>(ExclusiveContext *cx, double d);
+
+template JSAtom *
+js::NumberToAtom<NoGC>(ExclusiveContext *cx, double d);
 
 JSFlatString *
 js::NumberToString(JSContext *cx, double d)
 {
     if (JSString *str = js_NumberToStringWithBase<CanGC>(cx, d, 10))
         return &str->asFlat();
     return NULL;
 }
--- a/js/src/jsnum.h
+++ b/js/src/jsnum.h
@@ -37,31 +37,41 @@ js_InitNumberClass(JSContext *cx, js::Ha
 /*
  * String constants for global function names, used in jsapi.c and jsnum.c.
  */
 extern const char js_isNaN_str[];
 extern const char js_isFinite_str[];
 extern const char js_parseFloat_str[];
 extern const char js_parseInt_str[];
 
+class JSAtom;
+
+namespace js {
+
 /*
  * When base == 10, this function implements ToString() as specified by
  * ECMA-262-5 section 9.8.1; but note that it handles integers specially for
  * performance.  See also js::NumberToCString().
  */
 template <js::AllowGC allowGC>
 extern JSString *
-js_NumberToString(js::ThreadSafeContext *cx, double d);
+NumberToString(js::ThreadSafeContext *cx, double d);
 
-namespace js {
+template <js::AllowGC allowGC>
+extern JSAtom *
+NumberToAtom(js::ExclusiveContext *cx, double d);
 
 template <AllowGC allowGC>
 extern JSFlatString *
 Int32ToString(ThreadSafeContext *cx, int32_t i);
 
+template <AllowGC allowGC>
+extern JSAtom *
+Int32ToAtom(ExclusiveContext *cx, int32_t si);
+
 /*
  * Convert an integer or double (contained in the given value) to a string and
  * append to the given buffer.
  */
 extern bool JS_FASTCALL
 NumberValueToStringBuffer(JSContext *cx, const Value &v, StringBuffer &sb);
 
 /* Same as js_NumberToString, different signature. */
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -346,38 +346,58 @@ class JSObject : public js::ObjectImpl
     static inline bool updateSlotsForSpan(js::ExclusiveContext *cx,
                                           js::HandleObject obj, size_t oldSpan, size_t newSpan);
 
   public:
     /*
      * Trigger the write barrier on a range of slots that will no longer be
      * reachable.
      */
-    inline void prepareSlotRangeForOverwrite(size_t start, size_t end);
-    inline void prepareElementRangeForOverwrite(size_t start, size_t end);
+    void prepareSlotRangeForOverwrite(size_t start, size_t end) {
+        for (size_t i = start; i < end; i++)
+            getSlotAddressUnchecked(i)->js::HeapSlot::~HeapSlot();
+    }
+
+    void prepareElementRangeForOverwrite(size_t start, size_t end) {
+        JS_ASSERT(end <= getDenseInitializedLength());
+        for (size_t i = start; i < end; i++)
+            elements[i].js::HeapSlot::~HeapSlot();
+    }
 
     void rollbackProperties(js::ExclusiveContext *cx, uint32_t slotSpan);
 
-    inline void nativeSetSlot(uint32_t slot, const js::Value &value);
+    void nativeSetSlot(uint32_t slot, const js::Value &value) {
+        JS_ASSERT(uninlinedIsNative());
+        JS_ASSERT(slot < uninlinedSlotSpan());
+        return setSlot(slot, value);
+    }
+
     static inline void nativeSetSlotWithType(js::ExclusiveContext *cx,
                                              js::HandleObject, js::Shape *shape,
                                              const js::Value &value);
 
     inline const js::Value &getReservedSlot(uint32_t index) const {
         JS_ASSERT(index < JSSLOT_FREE(getClass()));
         return getSlot(index);
     }
 
     inline js::HeapSlot &getReservedSlotRef(uint32_t index) {
         JS_ASSERT(index < JSSLOT_FREE(getClass()));
         return getSlotRef(index);
     }
 
-    inline void initReservedSlot(uint32_t index, const js::Value &v);
-    inline void setReservedSlot(uint32_t index, const js::Value &v);
+    void initReservedSlot(uint32_t index, const js::Value &v) {
+        JS_ASSERT(index < JSSLOT_FREE(getClass()));
+        initSlot(index, v);
+    }
+
+    void setReservedSlot(uint32_t index, const js::Value &v) {
+        JS_ASSERT(index < JSSLOT_FREE(getClass()));
+        setSlot(index, v);
+    }
 
     /*
      * Marks this object as having a singleton type, and leave the type lazy.
      * Constructs a new, unique shape for the object.
      */
     static inline bool setSingletonType(js::ExclusiveContext *cx, js::HandleObject obj);
 
     // uninlinedGetType() is the same as getType(), but not inlined.
@@ -523,47 +543,79 @@ class JSObject : public js::ObjectImpl
     static inline bool isFrozen(JSContext *cx, js::HandleObject obj, bool *resultp) {
         return isSealedOrFrozen(cx, obj, FREEZE, resultp);
     }
 
     /* toString support. */
     static const char *className(JSContext *cx, js::HandleObject obj);
 
     /* Accessors for elements. */
-    inline bool ensureElements(js::ThreadSafeContext *cx, uint32_t capacity);
+    bool ensureElements(js::ThreadSafeContext *cx, uint32_t capacity) {
+        if (capacity > getDenseCapacity())
+            return growElements(cx, capacity);
+        return true;
+    }
+
     bool growElements(js::ThreadSafeContext *cx, uint32_t newcap);
     void shrinkElements(js::ThreadSafeContext *cx, uint32_t cap);
     void setDynamicElements(js::ObjectElements *header) {
         JS_ASSERT(!hasDynamicElements());
         elements = header->elements();
         JS_ASSERT(hasDynamicElements());
     }
 
     uint32_t getDenseCapacity() {
         JS_ASSERT(uninlinedIsNative());
         JS_ASSERT(getElementsHeader()->capacity >= getElementsHeader()->initializedLength);
         return getElementsHeader()->capacity;
     }
 
-    inline void setDenseInitializedLength(uint32_t length);
+    void setDenseInitializedLength(uint32_t length) {
+        JS_ASSERT(uninlinedIsNative());
+        JS_ASSERT(length <= getDenseCapacity());
+        prepareElementRangeForOverwrite(length, getElementsHeader()->initializedLength);
+        getElementsHeader()->initializedLength = length;
+    }
+
     inline void ensureDenseInitializedLength(js::ExclusiveContext *cx,
                                              uint32_t index, uint32_t extra);
-    inline void setDenseElement(uint32_t index, const js::Value &val);
-    inline void initDenseElement(uint32_t index, const js::Value &val);
-    inline void setDenseElementMaybeConvertDouble(uint32_t index, const js::Value &val);
+    void setDenseElement(uint32_t index, const js::Value &val) {
+        JS_ASSERT(uninlinedIsNative() && index < getDenseInitializedLength());
+        elements[index].set(this, js::HeapSlot::Element, index, val);
+    }
+
+    void initDenseElement(uint32_t index, const js::Value &val) {
+        JS_ASSERT(uninlinedIsNative() && index < getDenseInitializedLength());
+        elements[index].init(this, js::HeapSlot::Element, index, val);
+    }
+
+    void setDenseElementMaybeConvertDouble(uint32_t index, const js::Value &val) {
+        if (val.isInt32() && shouldConvertDoubleElements())
+            setDenseElement(index, js::DoubleValue(val.toInt32()));
+        else
+            setDenseElement(index, val);
+    }
+
     static inline void setDenseElementWithType(js::ExclusiveContext *cx, js::HandleObject obj,
                                                uint32_t index, const js::Value &val);
     static inline void initDenseElementWithType(js::ExclusiveContext *cx, js::HandleObject obj,
                                                 uint32_t index, const js::Value &val);
     static inline void setDenseElementHole(js::ExclusiveContext *cx,
                                            js::HandleObject obj, uint32_t index);
     static inline void removeDenseElementForSparseIndex(js::ExclusiveContext *cx,
                                                         js::HandleObject obj, uint32_t index);
     inline void copyDenseElements(uint32_t dstStart, const js::Value *src, uint32_t count);
-    inline void initDenseElements(uint32_t dstStart, const js::Value *src, uint32_t count);
+
+    void initDenseElements(uint32_t dstStart, const js::Value *src, uint32_t count) {
+        JS_ASSERT(dstStart + count <= getDenseCapacity());
+        JSRuntime *rt = runtimeFromMainThread();
+        for (uint32_t i = 0; i < count; ++i)
+            elements[dstStart + i].init(rt, this, js::HeapSlot::Element, dstStart + i, src[i]);
+    }
+
     inline void moveDenseElements(uint32_t dstStart, uint32_t srcStart, uint32_t count);
     inline void moveDenseElementsUnbarriered(uint32_t dstStart, uint32_t srcStart, uint32_t count);
 
     bool shouldConvertDoubleElements() {
         JS_ASSERT(uninlinedIsNative());
         return getElementsHeader()->shouldConvertDoubleElements();
     }
 
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -125,91 +125,22 @@ JSObject::canRemoveLastProperty()
     JS_ASSERT(!inDictionaryMode());
     js::Shape *previous = lastProperty()->previous().get();
     return previous->getObjectParent() == lastProperty()->getObjectParent()
         && previous->getObjectMetadata() == lastProperty()->getObjectMetadata()
         && previous->getObjectFlags() == lastProperty()->getObjectFlags();
 }
 
 inline void
-JSObject::setReservedSlot(uint32_t index, const js::Value &v)
-{
-    JS_ASSERT(index < JSSLOT_FREE(getClass()));
-    setSlot(index, v);
-}
-
-inline void
-JSObject::initReservedSlot(uint32_t index, const js::Value &v)
-{
-    JS_ASSERT(index < JSSLOT_FREE(getClass()));
-    initSlot(index, v);
-}
-
-inline void
-JSObject::prepareSlotRangeForOverwrite(size_t start, size_t end)
-{
-    for (size_t i = start; i < end; i++)
-        getSlotAddressUnchecked(i)->js::HeapSlot::~HeapSlot();
-}
-
-inline void
-JSObject::prepareElementRangeForOverwrite(size_t start, size_t end)
-{
-    JS_ASSERT(end <= getDenseInitializedLength());
-    for (size_t i = start; i < end; i++)
-        elements[i].js::HeapSlot::~HeapSlot();
-}
-
-inline void
-JSObject::setDenseInitializedLength(uint32_t length)
-{
-    JS_ASSERT(isNative());
-    JS_ASSERT(length <= getDenseCapacity());
-    prepareElementRangeForOverwrite(length, getElementsHeader()->initializedLength);
-    getElementsHeader()->initializedLength = length;
-}
-
-inline void
 JSObject::setShouldConvertDoubleElements()
 {
     JS_ASSERT(is<js::ArrayObject>() && !hasEmptyElements());
     getElementsHeader()->setShouldConvertDoubleElements();
 }
 
-inline bool
-JSObject::ensureElements(js::ThreadSafeContext *cx, uint32_t capacity)
-{
-    if (capacity > getDenseCapacity())
-        return growElements(cx, capacity);
-    return true;
-}
-
-inline void
-JSObject::setDenseElement(uint32_t index, const js::Value &val)
-{
-    JS_ASSERT(isNative() && index < getDenseInitializedLength());
-    elements[index].set(this, js::HeapSlot::Element, index, val);
-}
-
-inline void
-JSObject::setDenseElementMaybeConvertDouble(uint32_t index, const js::Value &val)
-{
-    if (val.isInt32() && shouldConvertDoubleElements())
-        setDenseElement(index, js::DoubleValue(val.toInt32()));
-    else
-        setDenseElement(index, val);
-}
-
-inline void
-JSObject::initDenseElement(uint32_t index, const js::Value &val)
-{
-    JS_ASSERT(isNative() && index < getDenseInitializedLength());
-    elements[index].init(this, js::HeapSlot::Element, index, val);
-}
-
 /* static */ inline void
 JSObject::setDenseElementWithType(js::ExclusiveContext *cx, js::HandleObject obj, uint32_t index,
                                   const js::Value &val)
 {
     js::types::AddTypePropertyId(cx, obj, JSID_VOID, val);
     obj->setDenseElementMaybeConvertDouble(index, val);
 }
 
@@ -245,25 +176,16 @@ JSObject::copyDenseElements(uint32_t dst
 {
     JS_ASSERT(dstStart + count <= getDenseCapacity());
     JS::Zone *zone = this->zone();
     for (uint32_t i = 0; i < count; ++i)
         elements[dstStart + i].set(zone, this, js::HeapSlot::Element, dstStart + i, src[i]);
 }
 
 inline void
-JSObject::initDenseElements(uint32_t dstStart, const js::Value *src, uint32_t count)
-{
-    JS_ASSERT(dstStart + count <= getDenseCapacity());
-    JSRuntime *rt = runtimeFromMainThread();
-    for (uint32_t i = 0; i < count; ++i)
-        elements[dstStart + i].init(rt, this, js::HeapSlot::Element, dstStart + i, src[i]);
-}
-
-inline void
 JSObject::moveDenseElements(uint32_t dstStart, uint32_t srcStart, uint32_t count)
 {
     JS_ASSERT(dstStart + count <= getDenseCapacity());
     JS_ASSERT(srcStart + count <= getDenseInitializedLength());
 
     /*
      * Using memmove here would skip write barriers. Also, we need to consider
      * an array containing [A, B, C], in the following situation:
@@ -642,24 +564,16 @@ JSObject::hasProperty(JSContext *cx, js:
     if (!lookupGeneric(cx, obj, id, &pobj, &prop)) {
         *foundp = false;  /* initialize to shut GCC up */
         return false;
     }
     *foundp = !!prop;
     return true;
 }
 
-inline void
-JSObject::nativeSetSlot(uint32_t slot, const js::Value &value)
-{
-    JS_ASSERT(isNative());
-    JS_ASSERT(slot < slotSpan());
-    return setSlot(slot, value);
-}
-
 /* static */ inline void
 JSObject::nativeSetSlotWithType(js::ExclusiveContext *cx, js::HandleObject obj, js::Shape *shape,
                                 const js::Value &value)
 {
     obj->nativeSetSlot(shape->slot(), value);
     js::types::AddTypePropertyId(cx, obj, shape->propid(), value);
 }
 
--- a/js/src/jspubtd.h
+++ b/js/src/jspubtd.h
@@ -167,40 +167,67 @@ typedef bool                    (*JSInit
 
 /*
  * Generic trace operation that calls JS_CallTracer on each traceable thing
  * stored in data.
  */
 typedef void
 (* JSTraceDataOp)(JSTracer *trc, void *data);
 
+namespace js {
+namespace gc {
+class StoreBuffer;
+}
+}
+
 namespace JS {
 
 typedef void (*OffThreadCompileCallback)(void *token, void *callbackData);
 
 namespace shadow {
 
 struct Runtime
 {
     /* Restrict zone access during Minor GC. */
     bool needsBarrier_;
 
 #ifdef JSGC_GENERATIONAL
     /* Allow inlining of Nursery::isInside. */
     uintptr_t gcNurseryStart_;
     uintptr_t gcNurseryEnd_;
+
+  private:
+    js::gc::StoreBuffer *gcStoreBufferPtr_;
 #endif
 
-    Runtime()
+  public:
+    Runtime(
+#ifdef JSGC_GENERATIONAL
+        js::gc::StoreBuffer *gcStoreBufferPtr
+#endif
+    )
       : needsBarrier_(false)
 #ifdef JSGC_GENERATIONAL
       , gcNurseryStart_(0)
       , gcNurseryEnd_(0)
+      , gcStoreBufferPtr_(gcStoreBufferPtr)
 #endif
     {}
+
+    bool needsBarrier() const {
+        return needsBarrier_;
+    }
+
+#ifdef JSGC_GENERATIONAL
+    js::gc::StoreBuffer *gcStoreBufferPtr() { return gcStoreBufferPtr_; }
+#endif
+
+    static JS::shadow::Runtime *asShadowRuntime(JSRuntime *rt) {
+        return reinterpret_cast<JS::shadow::Runtime*>(rt);
+    }
 };
 
 } /* namespace shadow */
 } /* namespace JS */
 
 namespace js {
 
 /*
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -34,16 +34,17 @@
 #include "vm/ArgumentsObject.h"
 #include "vm/Debugger.h"
 #include "vm/Interpreter.h"
 #include "vm/Shape.h"
 #include "vm/Xdr.h"
 
 #include "jsfuninlines.h"
 #include "jsinferinlines.h"
+#include "jsobjinlines.h"
 
 #include "vm/Runtime-inl.h"
 #include "vm/ScopeObject-inl.h"
 #include "vm/Stack-inl.h"
 
 using namespace js;
 using namespace js::gc;
 using namespace js::frontend;
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -2157,26 +2157,19 @@ FindReplaceLength(JSContext *cx, RegExpS
          */
         JS_ASSERT(rdata.lambda);
         JS_ASSERT(!rdata.elembase->getOps()->lookupProperty);
         JS_ASSERT(!rdata.elembase->getOps()->getProperty);
 
         RootedValue match(cx);
         if (!res->createLastMatch(cx, &match))
             return false;
-        JSString *str = match.toString();
-
-        JSAtom *atom;
-        if (str->isAtom()) {
-            atom = &str->asAtom();
-        } else {
-            atom = AtomizeString<CanGC>(cx, str);
-            if (!atom)
-                return false;
-        }
+        JSAtom *atom = ToAtom<CanGC>(cx, match);
+        if (!atom)
+            return false;
 
         RootedValue v(cx);
         if (HasDataProperty(cx, rdata.elembase, AtomToId(atom), v.address()) && v.isString()) {
             rdata.repstr = v.toString()->ensureLinear(cx);
             if (!rdata.repstr)
                 return false;
             *sizep = rdata.repstr->length();
             return true;
@@ -3864,17 +3857,17 @@ js::ToStringSlow(ExclusiveContext *cx, t
     }
 
     JSString *str;
     if (v.isString()) {
         str = v.toString();
     } else if (v.isInt32()) {
         str = Int32ToString<allowGC>(cx, v.toInt32());
     } else if (v.isDouble()) {
-        str = js_NumberToString<allowGC>(cx, v.toDouble());
+        str = NumberToString<allowGC>(cx, v.toDouble());
     } else if (v.isBoolean()) {
         str = js_BooleanToString(cx, v.toBoolean());
     } else if (v.isNull()) {
         str = cx->names().null;
     } else {
         str = cx->names().undefined;
     }
     return str;
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -65,25 +65,26 @@ EXPORTS.js += [
     '../public/LegacyIntTypes.h',
     '../public/MemoryMetrics.h',
     '../public/OldDebugAPI.h',
     '../public/ProfilingStack.h',
     '../public/PropertyKey.h',
     '../public/RequiredDefines.h',
     '../public/RootingAPI.h',
     '../public/StructuredClone.h',
+    '../public/Tracer.h',
     '../public/TypeDecls.h',
     '../public/Utility.h',
     '../public/Value.h',
     '../public/Vector.h',
 ]
 
 CPP_SOURCES += [
     'ArgumentsObject.cpp',
-    'TypedObject.cpp',
+    'Barrier.cpp',
     'BytecodeCompiler.cpp',
     'BytecodeEmitter.cpp',
     'CallNonGenericMethod.cpp',
     'CharacterEncoding.cpp',
     'DateTime.cpp',
     'Debugger.cpp',
     'Eval.cpp',
     'ExecutableAllocator.cpp',
@@ -127,18 +128,20 @@ CPP_SOURCES += [
     'Statistics.cpp',
     'StoreBuffer.cpp',
     'String.cpp',
     'StringBuffer.cpp',
     'StructuredClone.cpp',
     'TestingFunctions.cpp',
     'ThreadPool.cpp',
     'TokenStream.cpp',
+    'Tracer.cpp',
     'TypeRepresentation.cpp',
     'TypedArrayObject.cpp',
+    'TypedObject.cpp',
     'Unicode.cpp',
     'Value.cpp',
     'Verifier.cpp',
     'Xdr.cpp',
     'YarrCanonicalizeUCS2.cpp',
     'YarrInterpreter.cpp',
     'YarrPattern.cpp',
     'YarrSyntaxChecker.cpp',
deleted file mode 100644
--- a/js/src/vm/GlobalObject-inl.h
+++ /dev/null
@@ -1,158 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sts=4 et sw=4 tw=99:
- * 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 vm_GlobalObject_inl_h
-#define vm_GlobalObject_inl_h
-
-#include "vm/GlobalObject.h"
-
-#include "vm/ObjectImpl-inl.h"
-
-namespace js {
-
-inline void
-GlobalObject::setDetailsForKey(JSProtoKey key, JSObject *ctor, JSObject *proto)
-{
-    JS_ASSERT(getSlotRef(key).isUndefined());
-    JS_ASSERT(getSlotRef(JSProto_LIMIT + key).isUndefined());
-    JS_ASSERT(getSlotRef(2 * JSProto_LIMIT + key).isUndefined());
-    setSlot(key, ObjectValue(*ctor));
-    setSlot(JSProto_LIMIT + key, ObjectValue(*proto));
-    setSlot(2 * JSProto_LIMIT + key, ObjectValue(*ctor));
-}
-
-inline void
-GlobalObject::setObjectClassDetails(JSFunction *ctor, JSObject *proto)
-{
-    setDetailsForKey(JSProto_Object, ctor, proto);
-}
-
-inline void
-GlobalObject::setFunctionClassDetails(JSFunction *ctor, JSObject *proto)
-{
-    setDetailsForKey(JSProto_Function, ctor, proto);
-}
-
-void
-GlobalObject::setThrowTypeError(JSFunction *fun)
-{
-    JS_ASSERT(getSlotRef(THROWTYPEERROR).isUndefined());
-    setSlot(THROWTYPEERROR, ObjectValue(*fun));
-}
-
-void
-GlobalObject::setOriginalEval(JSObject *evalobj)
-{
-    JS_ASSERT(getSlotRef(EVAL).isUndefined());
-    setSlot(EVAL, ObjectValue(*evalobj));
-}
-
-void
-GlobalObject::setCreateArrayFromBufferHelper(uint32_t slot, Handle<JSFunction*> fun)
-{
-    JS_ASSERT(getSlotRef(slot).isUndefined());
-    setSlot(slot, ObjectValue(*fun));
-}
-
-void
-GlobalObject::setCreateDataViewForThis(Handle<JSFunction*> fun)
-{
-    JS_ASSERT(getSlotRef(CREATE_DATAVIEW_FOR_THIS).isUndefined());
-    setSlot(CREATE_DATAVIEW_FOR_THIS, ObjectValue(*fun));
-}
-
-template<>
-inline void
-GlobalObject::setCreateArrayFromBuffer<uint8_t>(Handle<JSFunction*> fun)
-{
-    setCreateArrayFromBufferHelper(FROM_BUFFER_UINT8, fun);
-}
-
-template<>
-inline void
-GlobalObject::setCreateArrayFromBuffer<int8_t>(Handle<JSFunction*> fun)
-{
-    setCreateArrayFromBufferHelper(FROM_BUFFER_INT8, fun);
-}
-
-template<>
-inline void
-GlobalObject::setCreateArrayFromBuffer<uint16_t>(Handle<JSFunction*> fun)
-{
-    setCreateArrayFromBufferHelper(FROM_BUFFER_UINT16, fun);
-}
-
-template<>
-inline void
-GlobalObject::setCreateArrayFromBuffer<int16_t>(Handle<JSFunction*> fun)
-{
-    setCreateArrayFromBufferHelper(FROM_BUFFER_INT16, fun);
-}
-
-template<>
-inline void
-GlobalObject::setCreateArrayFromBuffer<uint32_t>(Handle<JSFunction*> fun)
-{
-    setCreateArrayFromBufferHelper(FROM_BUFFER_UINT32, fun);
-}
-
-template<>
-inline void
-GlobalObject::setCreateArrayFromBuffer<int32_t>(Handle<JSFunction*> fun)
-{
-    setCreateArrayFromBufferHelper(FROM_BUFFER_INT32, fun);
-}
-
-template<>
-inline void
-GlobalObject::setCreateArrayFromBuffer<float>(Handle<JSFunction*> fun)
-{
-    setCreateArrayFromBufferHelper(FROM_BUFFER_FLOAT32, fun);
-}
-
-template<>
-inline void
-GlobalObject::setCreateArrayFromBuffer<double>(Handle<JSFunction*> fun)
-{
-    setCreateArrayFromBufferHelper(FROM_BUFFER_FLOAT64, fun);
-}
-
-template<>
-inline void
-GlobalObject::setCreateArrayFromBuffer<uint8_clamped>(Handle<JSFunction*> fun)
-{
-    setCreateArrayFromBufferHelper(FROM_BUFFER_UINT8CLAMPED, fun);
-}
-
-void
-GlobalObject::setProtoGetter(JSFunction *protoGetter)
-{
-    JS_ASSERT(getSlotRef(PROTO_GETTER).isUndefined());
-    setSlot(PROTO_GETTER, ObjectValue(*protoGetter));
-}
-
-bool
-GlobalObject::setIntrinsicValue(JSContext *cx, PropertyName *name, HandleValue value)
-{
-#ifdef DEBUG
-    RootedObject self(cx, this);
-    JS_ASSERT(cx->runtime()->isSelfHostingGlobal(self));
-#endif
-    RootedObject holder(cx, intrinsicsHolder());
-    RootedValue valCopy(cx, value);
-    return JSObject::setProperty(cx, holder, holder, name, &valCopy, false);
-}
-
-void
-GlobalObject::setIntrinsicsHolder(JSObject *obj)
-{
-    JS_ASSERT(getSlotRef(INTRINSICS).isUndefined());
-    setSlot(INTRINSICS, ObjectValue(*obj));
-}
-
-} // namespace js
-
-#endif /* vm_GlobalObject_inl_h */
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -1,15 +1,15 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * 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 "vm/GlobalObject-inl.h"
+#include "vm/GlobalObject.h"
 
 #include "jscntxt.h"
 #include "jsdate.h"
 #include "jsexn.h"
 #include "jsfriendapi.h"
 #include "jsmath.h"
 #include "json.h"
 #include "jsweakmap.h"
--- a/js/src/vm/GlobalObject.h
+++ b/js/src/vm/GlobalObject.h
@@ -119,25 +119,52 @@ class GlobalObject : public JSObject
     ::js_InitObjectClass(JSContext *cx, js::HandleObject);
     friend JSObject *
     ::js_InitFunctionClass(JSContext *cx, js::HandleObject);
 
     /* Initialize the Function and Object classes.  Must only be called once! */
     JSObject *
     initFunctionAndObjectClasses(JSContext *cx);
 
-    inline void setDetailsForKey(JSProtoKey key, JSObject *ctor, JSObject *proto);
-    inline void setObjectClassDetails(JSFunction *ctor, JSObject *proto);
-    inline void setFunctionClassDetails(JSFunction *ctor, JSObject *proto);
+    void setDetailsForKey(JSProtoKey key, JSObject *ctor, JSObject *proto) {
+        JS_ASSERT(getSlotRef(key).isUndefined());
+        JS_ASSERT(getSlotRef(JSProto_LIMIT + key).isUndefined());
+        JS_ASSERT(getSlotRef(2 * JSProto_LIMIT + key).isUndefined());
+        setSlot(key, ObjectValue(*ctor));
+        setSlot(JSProto_LIMIT + key, ObjectValue(*proto));
+        setSlot(2 * JSProto_LIMIT + key, ObjectValue(*ctor));
+    }
+
+    void setObjectClassDetails(JSFunction *ctor, JSObject *proto) {
+        setDetailsForKey(JSProto_Object, ctor, proto);
+    }
+
+    void setFunctionClassDetails(JSFunction *ctor, JSObject *proto) {
+        setDetailsForKey(JSProto_Function, ctor, proto);
+    }
 
-    inline void setThrowTypeError(JSFunction *fun);
-    inline void setOriginalEval(JSObject *evalobj);
-    inline void setProtoGetter(JSFunction *protoGetter);
+    void setThrowTypeError(JSFunction *fun) {
+        JS_ASSERT(getSlotRef(THROWTYPEERROR).isUndefined());
+        setSlot(THROWTYPEERROR, ObjectValue(*fun));
+    }
+
+    void setOriginalEval(JSObject *evalobj) {
+        JS_ASSERT(getSlotRef(EVAL).isUndefined());
+        setSlot(EVAL, ObjectValue(*evalobj));
+    }
 
-    inline void setIntrinsicsHolder(JSObject *obj);
+    void setProtoGetter(JSFunction *protoGetter) {
+        JS_ASSERT(getSlotRef(PROTO_GETTER).isUndefined());
+        setSlot(PROTO_GETTER, ObjectValue(*protoGetter));
+    }
+
+    void setIntrinsicsHolder(JSObject *obj) {
+        JS_ASSERT(getSlotRef(INTRINSICS).isUndefined());
+        setSlot(INTRINSICS, ObjectValue(*obj));
+    }
 
     Value getConstructor(JSProtoKey key) const {
         JS_ASSERT(key <= JSProto_LIMIT);
         return getSlot(key);
     }
 
     Value getPrototype(JSProtoKey key) const {
         JS_ASSERT(key <= JSProto_LIMIT);
@@ -188,21 +215,27 @@ class GlobalObject : public JSObject
     }
 
     Value createArrayFromBufferHelper(uint32_t slot) const {
         JS_ASSERT(typedArrayClassesInitialized());
         JS_ASSERT(FROM_BUFFER_UINT8 <= slot && slot <= FROM_BUFFER_UINT8CLAMPED);
         return getSlot(slot);
     }
 
-    inline void setCreateArrayFromBufferHelper(uint32_t slot, Handle<JSFunction*> fun);
+    void setCreateArrayFromBufferHelper(uint32_t slot, Handle<JSFunction*> fun) {
+        JS_ASSERT(getSlotRef(slot).isUndefined());
+        setSlot(slot, ObjectValue(*fun));
+    }
 
   public:
     /* XXX Privatize me! */
-    inline void setCreateDataViewForThis(Handle<JSFunction*> fun);
+    void setCreateDataViewForThis(Handle<JSFunction*> fun) {
+        JS_ASSERT(getSlotRef(CREATE_DATAVIEW_FOR_THIS).isUndefined());
+        setSlot(CREATE_DATAVIEW_FOR_THIS, ObjectValue(*fun));
+    }
 
     template<typename T>
     inline void setCreateArrayFromBuffer(Handle<JSFunction*> fun);
 
   public:
     static GlobalObject *create(JSContext *cx, Class *clasp);
 
     /*
@@ -411,19 +444,30 @@ class GlobalObject : public JSObject
             return true;
         if (!cx->runtime()->cloneSelfHostedValue(cx, name, value))
             return false;
         mozilla::DebugOnly<bool> ok = JS_DefinePropertyById(cx, holder, id, value, NULL, NULL, 0);
         JS_ASSERT(ok);
         return true;
     }
 
-    inline bool setIntrinsicValue(JSContext *cx, PropertyName *name, HandleValue value);
+    bool setIntrinsicValue(JSContext *cx, PropertyName *name, HandleValue value) {
+#ifdef DEBUG
+        RootedObject self(cx, this);
+        JS_ASSERT(cx->runtime()->isSelfHostingGlobal(self));
+#endif
+        RootedObject holder(cx, intrinsicsHolder());
+        RootedValue valCopy(cx, value);
+        return JSObject::setProperty(cx, holder, holder, name, &valCopy, false);
+    }
 
-    inline RegExpStatics *getRegExpStatics() const;
+    RegExpStatics *getRegExpStatics() const {
+        JSObject &resObj = getSlot(REGEXP_STATICS).toObject();
+        return static_cast<RegExpStatics *>(resObj.getPrivate());
+    }
 
     JSObject *getThrowTypeError() const {
         JS_ASSERT(functionObjectClassesInitialized());
         return &getSlot(THROWTYPEERROR).toObject();
     }
 
     Value createDataViewForThis() const {
         JS_ASSERT(dataViewClassInitialized());
@@ -478,16 +522,79 @@ class GlobalObject : public JSObject
      * exist. Returns NULL only on OOM.
      */
     static DebuggerVector *getOrCreateDebuggers(JSContext *cx, Handle<GlobalObject*> global);
 
     static bool addDebugger(JSContext *cx, Handle<GlobalObject*> global, Debugger *dbg);
 };
 
 template<>
+inline void
+GlobalObject::setCreateArrayFromBuffer<uint8_t>(Handle<JSFunction*> fun)
+{
+    setCreateArrayFromBufferHelper(FROM_BUFFER_UINT8, fun);
+}
+
+template<>
+inline void
+GlobalObject::setCreateArrayFromBuffer<int8_t>(Handle<JSFunction*> fun)
+{
+    setCreateArrayFromBufferHelper(FROM_BUFFER_INT8, fun);
+}
+
+template<>
+inline void
+GlobalObject::setCreateArrayFromBuffer<uint16_t>(Handle<JSFunction*> fun)
+{
+    setCreateArrayFromBufferHelper(FROM_BUFFER_UINT16, fun);
+}
+
+template<>
+inline void
+GlobalObject::setCreateArrayFromBuffer<int16_t>(Handle<JSFunction*> fun)
+{
+    setCreateArrayFromBufferHelper(FROM_BUFFER_INT16, fun);
+}
+
+template<>
+inline void
+GlobalObject::setCreateArrayFromBuffer<uint32_t>(Handle<JSFunction*> fun)
+{
+    setCreateArrayFromBufferHelper(FROM_BUFFER_UINT32, fun);
+}
+
+template<>
+inline void
+GlobalObject::setCreateArrayFromBuffer<int32_t>(Handle<JSFunction*> fun)
+{
+    setCreateArrayFromBufferHelper(FROM_BUFFER_INT32, fun);
+}
+
+template<>
+inline void
+GlobalObject::setCreateArrayFromBuffer<float>(Handle<JSFunction*> fun)
+{
+    setCreateArrayFromBufferHelper(FROM_BUFFER_FLOAT32, fun);
+}
+
+template<>
+inline void
+GlobalObject::setCreateArrayFromBuffer<double>(Handle<JSFunction*> fun)
+{
+    setCreateArrayFromBufferHelper(FROM_BUFFER_FLOAT64, fun);
+}
+
+template<>
+inline void
+GlobalObject::setCreateArrayFromBuffer<uint8_clamped>(Handle<JSFunction*> fun)
+{
+    setCreateArrayFromBufferHelper(FROM_BUFFER_UINT8CLAMPED, fun);
+}
+
+template<>
 inline Value
 GlobalObject::createArrayFromBuffer<uint8_t>() const
 {
     return createArrayFromBufferHelper(FROM_BUFFER_UINT8);
 }
 
 template<>
 inline Value
--- a/js/src/vm/Interpreter-inl.h
+++ b/js/src/vm/Interpreter-inl.h
@@ -18,17 +18,16 @@
 #include "jit/IonCompartment.h"
 #include "vm/ArgumentsObject.h"
 #include "vm/ForkJoin.h"
 
 #include "jsatominlines.h"
 #include "jsinferinlines.h"
 #include "jsobjinlines.h"
 
-#include "vm/GlobalObject-inl.h"
 #include "vm/Stack-inl.h"
 
 namespace js {
 
 inline bool
 ComputeThis(JSContext *cx, AbstractFramePtr frame)
 {
     JS_ASSERT_IF(frame.isStackFrame(), !frame.asStackFrame()->runningInJit());
--- a/js/src/vm/ObjectImpl-inl.h
+++ b/js/src/vm/ObjectImpl-inl.h
@@ -62,78 +62,16 @@ js::ObjectImpl::isExtensible(ExclusiveCo
 }
 
 inline bool
 js::ObjectImpl::isNative() const
 {
     return lastProperty()->isNative();
 }
 
-#ifdef DEBUG
-inline bool
-IsObjectValueInCompartment(js::Value v, JSCompartment *comp)
-{
-    if (!v.isObject())
-        return true;
-    return v.toObject().compartment() == comp;
-}
-#endif
-
-inline void
-js::ObjectImpl::setSlot(uint32_t slot, const js::Value &value)
-{
-    MOZ_ASSERT(slotInRange(slot));
-    MOZ_ASSERT(IsObjectValueInCompartment(value, asObjectPtr()->compartment()));
-    getSlotRef(slot).set(this->asObjectPtr(), HeapSlot::Slot, slot, value);
-}
-
-inline void
-js::ObjectImpl::setCrossCompartmentSlot(uint32_t slot, const js::Value &value)
-{
-    MOZ_ASSERT(slotInRange(slot));
-    getSlotRef(slot).set(this->asObjectPtr(), HeapSlot::Slot, slot, value);
-}
-
-inline void
-js::ObjectImpl::initSlot(uint32_t slot, const js::Value &value)
-{
-    MOZ_ASSERT(getSlot(slot).isUndefined());
-    MOZ_ASSERT(slotInRange(slot));
-    MOZ_ASSERT(IsObjectValueInCompartment(value, asObjectPtr()->compartment()));
-    initSlotUnchecked(slot, value);
-}
-
-inline void
-js::ObjectImpl::initCrossCompartmentSlot(uint32_t slot, const js::Value &value)
-{
-    MOZ_ASSERT(getSlot(slot).isUndefined());
-    MOZ_ASSERT(slotInRange(slot));
-    initSlotUnchecked(slot, value);
-}
-
-inline void
-js::ObjectImpl::initSlotUnchecked(uint32_t slot, const js::Value &value)
-{
-    getSlotAddressUnchecked(slot)->init(this->asObjectPtr(), HeapSlot::Slot, slot, value);
-}
-
-inline void
-js::ObjectImpl::setFixedSlot(uint32_t slot, const js::Value &value)
-{
-    MOZ_ASSERT(slot < numFixedSlots());
-    fixedSlots()[slot].set(this->asObjectPtr(), HeapSlot::Slot, slot, value);
-}
-
-inline void
-js::ObjectImpl::initFixedSlot(uint32_t slot, const js::Value &value)
-{
-    MOZ_ASSERT(slot < numFixedSlots());
-    fixedSlots()[slot].init(this->asObjectPtr(), HeapSlot::Slot, slot, value);
-}
-
 inline uint32_t
 js::ObjectImpl::slotSpan() const
 {
     if (inDictionaryMode())
         return lastProperty()->base()->slotSpan();
     return lastProperty()->slotSpan();
 }
 
@@ -183,24 +121,16 @@ js::ObjectImpl::privateWriteBarrierPre(v
     Zone *zone = this->zone();
     if (zone->needsBarrier()) {
         if (*old && getClass()->trace)
             getClass()->trace(zone->barrierTracer(), this->asObjectPtr());
     }
 #endif
 }
 
-inline void
-js::ObjectImpl::privateWriteBarrierPost(void **pprivate)
-{
-#ifdef JSGC_GENERATIONAL
-    runtimeFromAnyThread()->gcStoreBuffer.putCell(reinterpret_cast<js::gc::Cell **>(pprivate));
-#endif
-}
-
 /* static */ inline void
 js::ObjectImpl::writeBarrierPre(ObjectImpl *obj)
 {
 #ifdef JSGC_INCREMENTAL
     /*
      * This would normally be a null test, but TypeScript::global uses 0x1 as a
      * special value.
      */
@@ -212,42 +142,16 @@ js::ObjectImpl::writeBarrierPre(ObjectIm
         MOZ_ASSERT(!zone->runtimeFromMainThread()->isHeapMajorCollecting());
         JSObject *tmp = obj->asObjectPtr();
         MarkObjectUnbarriered(zone->barrierTracer(), &tmp, "write barrier");
         MOZ_ASSERT(tmp == obj->asObjectPtr());
     }
 #endif
 }
 
-/* static */ inline void
-js::ObjectImpl::writeBarrierPost(ObjectImpl *obj, void *addr)
-{
-#ifdef JSGC_GENERATIONAL
-    if (IsNullTaggedPointer(obj))
-        return;
-    obj->runtimeFromAnyThread()->gcStoreBuffer.putCell((Cell **)addr);
-#endif
-}
-
-/* static */ inline void
-js::ObjectImpl::writeBarrierPostRelocate(ObjectImpl *obj, void *addr)
-{
-#ifdef JSGC_GENERATIONAL
-    obj->runtimeFromAnyThread()->gcStoreBuffer.putRelocatableCell((Cell **)addr);
-#endif
-}
-
-/* static */ inline void
-js::ObjectImpl::writeBarrierPostRemove(ObjectImpl *obj, void *addr)
-{
-#ifdef JSGC_GENERATIONAL
-    obj->runtimeFromAnyThread()->gcStoreBuffer.removeRelocatableCell((Cell **)addr);
-#endif
-}
-
 inline void
 js::ObjectImpl::setPrivate(void *data)
 {
     void **pprivate = &privateRef(numFixedSlots());
     privateWriteBarrierPre(pprivate);
     *pprivate = data;
 }
 
--- a/js/src/vm/ObjectImpl.cpp
+++ b/js/src/vm/ObjectImpl.cpp
@@ -11,16 +11,22 @@
 #include "vm/Debugger.h"
 
 #include "jsobjinlines.h"
 
 #include "gc/Barrier-inl.h"
 
 using namespace js;
 
+JSCompartment *
+js::ObjectImpl::uninlinedCompartment() const
+{
+    return compartment();
+}
+
 bool
 js::ObjectImpl::uninlinedIsNative() const
 {
     return isNative();
 }
 
 uint32_t
 js::ObjectImpl::uninlinedSlotSpan() const
--- a/js/src/vm/ObjectImpl.h
+++ b/js/src/vm/ObjectImpl.h
@@ -13,16 +13,17 @@
 #include <stdint.h>
 
 #include "jsfriendapi.h"
 #include "jsinfer.h"
 #include "NamespaceImports.h"
 
 #include "gc/Barrier.h"
 #include "gc/Heap.h"
+#include "gc/Marking.h"
 #include "js/Value.h"
 #include "vm/NumericConversions.h"
 #include "vm/String.h"
 
 #define JSSLOT_FREE(clasp)  JSCLASS_RESERVED_SLOTS(clasp)
 
 namespace js {
 
@@ -1140,16 +1141,21 @@ struct ObjectOps;
 class Shape;
 
 class NewObjectCache;
 class TaggedProto;
 
 inline Value
 ObjectValue(ObjectImpl &obj);
 
+#ifdef DEBUG
+static inline bool
+IsObjectValueInCompartment(js::Value v, JSCompartment *comp);
+#endif
+
 /*
  * ObjectImpl specifies the internal implementation of an object.  (In contrast
  * JSObject specifies an "external" interface, at the conceptual level of that
  * exposed in ECMAScript.)
  *
  * The |shape_| member stores the shape of the object, which includes the
  * object's class and the layout of all its properties.
  *
@@ -1192,17 +1198,17 @@ ObjectValue(ObjectImpl &obj);
  * will change so that some members are private, and only certain methods that
  * act upon them will be protected.
  */
 class ObjectImpl : public gc::Cell
 {
   protected:
     /*
      * Shape of the object, encodes the layout of the object's properties and
-     * all other information about its structure. See jsscope.h.
+     * all other information about its structure. See vm/Shape.h.
      */
     HeapPtrShape shape_;
 
     /*
      * The object's type and prototype. For objects with the LAZY_TYPE flag
      * set, this is the prototype's default 'new' type and can only be used
      * to get that prototype.
      */
@@ -1431,17 +1437,19 @@ class ObjectImpl : public gc::Cell
         MOZ_ASSERT(shape_);
         return shape_;
     }
 
     bool generateOwnShape(ExclusiveContext *cx, js::Shape *newShape = NULL) {
         return replaceWithNewEquivalentShape(cx, lastProperty(), newShape);
     }
 
+    // uninlinedCompartment() is equivalent to compartment(), but isn't inlined.
     inline JSCompartment *compartment() const;
+    JSCompartment *uninlinedCompartment() const;
 
     // uninlinedIsNative() is equivalent to isNative(), but isn't inlined.
     inline bool isNative() const;
     bool uninlinedIsNative() const;
 
     types::TypeObject *type() const {
         MOZ_ASSERT(!hasLazyType());
         return type_;
@@ -1568,36 +1576,65 @@ class ObjectImpl : public gc::Cell
         JS_ASSERT(uninlinedIsNative() && slot < uninlinedSlotSpan());
         return getSlotRef(slot);
     }
     const Value &nativeGetSlot(uint32_t slot) const {
         JS_ASSERT(uninlinedIsNative() && slot < uninlinedSlotSpan());
         return getSlot(slot);
     }
 
-    inline void setSlot(uint32_t slot, const Value &value);
-    inline void setCrossCompartmentSlot(uint32_t slot, const Value &value);
-    inline void initSlot(uint32_t slot, const Value &value);
-    inline void initCrossCompartmentSlot(uint32_t slot, const Value &value);
-    inline void initSlotUnchecked(uint32_t slot, const Value &value);
+    void setSlot(uint32_t slot, const Value &value) {
+        MOZ_ASSERT(slotInRange(slot));
+        MOZ_ASSERT(IsObjectValueInCompartment(value, uninlinedCompartment()));
+        getSlotRef(slot).set(this->asObjectPtr(), HeapSlot::Slot, slot, value);
+    }
+
+    inline void setCrossCompartmentSlot(uint32_t slot, const Value &value) {
+        MOZ_ASSERT(slotInRange(slot));
+        getSlotRef(slot).set(this->asObjectPtr(), HeapSlot::Slot, slot, value);
+    }
+
+    void initSlot(uint32_t slot, const Value &value) {
+        MOZ_ASSERT(getSlot(slot).isUndefined());
+        MOZ_ASSERT(slotInRange(slot));
+        MOZ_ASSERT(IsObjectValueInCompartment(value, uninlinedCompartment()));
+        initSlotUnchecked(slot, value);
+    }
+
+    void initCrossCompartmentSlot(uint32_t slot, const Value &value) {
+        MOZ_ASSERT(getSlot(slot).isUndefined());
+        MOZ_ASSERT(slotInRange(slot));
+        initSlotUnchecked(slot, value);
+    }
+
+    void initSlotUnchecked(uint32_t slot, const Value &value) {
+        getSlotAddressUnchecked(slot)->init(this->asObjectPtr(), HeapSlot::Slot, slot, value);
+    }
 
     /* For slots which are known to always be fixed, due to the way they are allocated. */
 
     HeapSlot &getFixedSlotRef(uint32_t slot) {
         MOZ_ASSERT(slot < numFixedSlots());
         return fixedSlots()[slot];
     }
 
     const Value &getFixedSlot(uint32_t slot) const {
         MOZ_ASSERT(slot < numFixedSlots());
         return fixedSlots()[slot];
     }
 
-    inline void setFixedSlot(uint32_t slot, const Value &value);
-    inline void initFixedSlot(uint32_t slot, const Value &value);
+    void setFixedSlot(uint32_t slot, const Value &value) {
+        MOZ_ASSERT(slot < numFixedSlots());
+        fixedSlots()[slot].set(this->asObjectPtr(), HeapSlot::Slot, slot, value);
+    }
+
+    void initFixedSlot(uint32_t slot, const Value &value) {
+        MOZ_ASSERT(slot < numFixedSlots());
+        fixedSlots()[slot].init(this->asObjectPtr(), HeapSlot::Slot, slot, value);
+    }
 
     /*
      * Get the number of dynamic slots to allocate to cover the properties in
      * an object with the given number of fixed slots and slot span. The slot
      * capacity is not stored explicitly, and the allocated size of the slot
      * array is kept in sync with this count.
      */
     static uint32_t dynamicSlotsCount(uint32_t nfixed, uint32_t span) {
@@ -1653,24 +1690,49 @@ class ObjectImpl : public gc::Cell
     }
 
     inline bool hasEmptyElements() const {
         return elements == emptyObjectElements;
     }
 
     /* GC support. */
     JS_ALWAYS_INLINE Zone *zone() const;
-    static inline ThingRootKind rootKind() { return THING_ROOT_OBJECT; }
+    static ThingRootKind rootKind() { return THING_ROOT_OBJECT; }
+
     static inline void readBarrier(ObjectImpl *obj);
     static inline void writeBarrierPre(ObjectImpl *obj);
-    static inline void writeBarrierPost(ObjectImpl *obj, void *addr);
-    static inline void writeBarrierPostRelocate(ObjectImpl *obj, void *addr);
-    static inline void writeBarrierPostRemove(ObjectImpl *obj, void *addr);
+
+    static void writeBarrierPost(ObjectImpl *obj, void *addr) {
+#ifdef JSGC_GENERATIONAL
+        if (IsNullTaggedPointer(obj))
+            return;
+        obj->shadowRuntimeFromAnyThread()->gcStoreBufferPtr()->putCell((Cell **)addr);
+#endif
+    }
+
+    static void writeBarrierPostRelocate(ObjectImpl *obj, void *addr) {
+#ifdef JSGC_GENERATIONAL
+        obj->shadowRuntimeFromAnyThread()->gcStoreBufferPtr()->putRelocatableCell((Cell **)addr);
+#endif
+    }
+
+    static void writeBarrierPostRemove(ObjectImpl *obj, void *addr) {
+#ifdef JSGC_GENERATIONAL
+        obj->shadowRuntimeFromAnyThread()->gcStoreBufferPtr()->removeRelocatableCell((Cell **)addr);
+#endif
+    }
+
     inline void privateWriteBarrierPre(void **oldval);
-    inline void privateWriteBarrierPost(void **pprivate);
+
+    void privateWriteBarrierPost(void **pprivate) {
+#ifdef JSGC_GENERATIONAL
+        shadowRuntimeFromAnyThread()->gcStoreBufferPtr()->putCell(reinterpret_cast<js::gc::Cell **>(pprivate));
+#endif
+    }
+
     void markChildren(JSTracer *trc);
 
     /* Private data accessors. */
 
     inline void *&privateRef(uint32_t nfixed) const { /* XXX should be private, not protected! */
         /*
          * The private pointer of an object can hold any word sized value.
          * Private pointers are stored immediately after the last fixed slot of
@@ -1731,16 +1793,26 @@ ObjectValue(ObjectImpl &obj)
 }
 
 inline Handle<JSObject*>
 Downcast(Handle<ObjectImpl*> obj)
 {
     return Handle<JSObject*>::fromMarkedLocation(reinterpret_cast<JSObject* const*>(obj.address()));
 }
 
+#ifdef DEBUG
+static inline bool
+IsObjectValueInCompartment(js::Value v, JSCompartment *comp)
+{
+    if (!v.isObject())
+        return true;
+    return reinterpret_cast<ObjectImpl*>(&v.toObject())->uninlinedCompartment() == comp;
+}
+#endif
+
 extern JSObject *
 ArrayBufferDelegate(JSContext *cx, Handle<ObjectImpl*> obj);
 
 /* Generic [[GetOwnProperty]] method. */
 bool
 GetOwnElement(JSContext *cx, Handle<ObjectImpl*> obj, uint32_t index, unsigned resolveFlags,
               PropDesc *desc);
 extern bool
--- a/js/src/vm/RegExpObject-inl.h
+++ b/js/src/vm/RegExpObject-inl.h
@@ -13,53 +13,11 @@ namespace js {
 
 inline void
 RegExpObject::setShared(ExclusiveContext *cx, RegExpShared &shared)
 {
     shared.prepareForUse(cx);
     JSObject::setPrivate(&shared);
 }
 
-inline void
-RegExpObject::setLastIndex(double d)
-{
-    setSlot(LAST_INDEX_SLOT, NumberValue(d));
-}
-
-inline void
-RegExpObject::zeroLastIndex()
-{
-    setSlot(LAST_INDEX_SLOT, Int32Value(0));
-}
-
-inline void
-RegExpObject::setSource(JSAtom *source)
-{
-    setSlot(SOURCE_SLOT, StringValue(source));
-}
-
-inline void
-RegExpObject::setIgnoreCase(bool enabled)
-{
-    setSlot(IGNORE_CASE_FLAG_SLOT, BooleanValue(enabled));
-}
-
-inline void
-RegExpObject::setGlobal(bool enabled)
-{
-    setSlot(GLOBAL_FLAG_SLOT, BooleanValue(enabled));
-}
-
-inline void
-RegExpObject::setMultiline(bool enabled)
-{
-    setSlot(MULTILINE_FLAG_SLOT, BooleanValue(enabled));
-}
-
-inline void
-RegExpObject::setSticky(bool enabled)
-{
-    setSlot(STICKY_FLAG_SLOT, BooleanValue(enabled));
-}
-
 } /* namespace js */
 
 #endif /* vm_RegExpObject_inl_h */
--- a/js/src/vm/RegExpObject.h
+++ b/js/src/vm/RegExpObject.h
@@ -361,39 +361,60 @@ class RegExpObject : public JSObject
     static RegExpObject *
     createNoStatics(ExclusiveContext *cx, HandleAtom atom, RegExpFlag flags, frontend::TokenStream *ts);
 
     /* Accessors. */
 
     static unsigned lastIndexSlot() { return LAST_INDEX_SLOT; }
 
     const Value &getLastIndex() const { return getSlot(LAST_INDEX_SLOT); }
-    inline void setLastIndex(double d);
-    inline void zeroLastIndex();
+
+    void setLastIndex(double d) {
+        setSlot(LAST_INDEX_SLOT, NumberValue(d));
+    }
+
+    void zeroLastIndex() {
+        setSlot(LAST_INDEX_SLOT, Int32Value(0));
+    }
 
     JSFlatString *toString(JSContext *cx) const;
 
     JSAtom *getSource() const { return &getSlot(SOURCE_SLOT).toString()->asAtom(); }
-    inline void setSource(JSAtom *source);
+
+    void setSource(JSAtom *source) {
+        setSlot(SOURCE_SLOT, StringValue(source));
+    }
 
     RegExpFlag getFlags() const {
         unsigned flags = 0;
         flags |= global() ? GlobalFlag : 0;
         flags |= ignoreCase() ? IgnoreCaseFlag : 0;
         flags |= multiline() ? MultilineFlag : 0;
         flags |= sticky() ? StickyFlag : 0;
         return RegExpFlag(flags);
     }
 
     /* Flags. */
 
-    inline void setIgnoreCase(bool enabled);
-    inline void setGlobal(bool enabled);
-    inline void setMultiline(bool enabled);
-    inline void setSticky(bool enabled);
+    void setIgnoreCase(bool enabled) {
+        setSlot(IGNORE_CASE_FLAG_SLOT, BooleanValue(enabled));
+    }
+
+    void setGlobal(bool enabled) {
+        setSlot(GLOBAL_FLAG_SLOT, BooleanValue(enabled));
+    }
+
+    void setMultiline(bool enabled) {
+        setSlot(MULTILINE_FLAG_SLOT, BooleanValue(enabled));
+    }
+
+    void setSticky(bool enabled) {
+        setSlot(STICKY_FLAG_SLOT, BooleanValue(enabled));
+    }
+
     bool ignoreCase() const { return getSlot(IGNORE_CASE_FLAG_SLOT).toBoolean(); }
     bool global() const     { return getSlot(GLOBAL_FLAG_SLOT).toBoolean(); }
     bool multiline() const  { return getSlot(MULTILINE_FLAG_SLOT).toBoolean(); }
     bool sticky() const     { return getSlot(STICKY_FLAG_SLOT).toBoolean(); }
 
     void shared(RegExpGuard *g) const {
         JS_ASSERT(maybeShared() != NULL);
         g->init(*maybeShared());
@@ -401,16 +422,17 @@ class RegExpObject : public JSObject
 
     bool getShared(ExclusiveContext *cx, RegExpGuard *g) {
         if (RegExpShared *shared = maybeShared()) {
             g->init(*shared);
             return true;
         }
         return createShared(cx, g);
     }
+
     inline void setShared(ExclusiveContext *cx, RegExpShared &shared);
 
   private:
     friend class RegExpObjectBuilder;
 
     /*
      * Compute the initial shape to associate with fresh RegExp objects,
      * encoding their initial properties. Return the shape after
--- a/js/src/vm/RegExpStatics.h
+++ b/js/src/vm/RegExpStatics.h
@@ -195,23 +195,16 @@ class PreserveRegExpStatics
 
     bool init(JSContext *cx) {
         return original->save(cx, &buffer.getStatics());
     }
 
     ~PreserveRegExpStatics() { original->restore(); }
 };
 
-inline js::RegExpStatics *
-js::GlobalObject::getRegExpStatics() const
-{
-    JSObject &resObj = getSlot(REGEXP_STATICS).toObject();
-    return static_cast<RegExpStatics *>(resObj.getPrivate());
-}
-
 inline bool
 RegExpStatics::createDependent(JSContext *cx, size_t start, size_t end, MutableHandleValue out)
 {
     /* Private function: caller must perform lazy evaluation. */
     JS_ASSERT(!pendingLazyEvaluation);
 
     JS_ASSERT(start <= end);
     JS_ASSERT(end <= matchesInput->length());
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -93,17 +93,22 @@ PerThreadData::addToThreadList()
 void
 PerThreadData::removeFromThreadList()
 {
     JS_ASSERT(CurrentThreadCanAccessRuntime(runtime_));
     removeFrom(runtime_->threadList);
 }
 
 JSRuntime::JSRuntime(JSUseHelperThreads useHelperThreads)
-  : mainThread(this),
+  : JS::shadow::Runtime(
+#ifdef JSGC_GENERATIONAL
+        &gcStoreBuffer
+#endif
+    ),
+    mainThread(this),
     interrupt(0),
     handlingSignal(false),
     operationCallback(NULL),
 #ifdef JS_THREADSAFE
     operationCallbackLock(NULL),
 #ifdef DEBUG
     operationCallbackOwner(NULL),
 #endif
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -1221,20 +1221,16 @@ struct JSRuntime : public JS::shadow::Ru
      */
     volatile ptrdiff_t  gcMallocBytes;
 
   public:
     void setNeedsBarrier(bool needs) {
         needsBarrier_ = needs;
     }
 
-    bool needsBarrier() const {
-        return needsBarrier_;
-    }
-
     struct ExtraTracer {
         JSTraceDataOp op;
         void *data;
 
         ExtraTracer()
           : op(NULL), data(NULL)
         {}
         ExtraTracer(JSTraceDataOp op, void *data)
--- a/js/src/vm/ScopeObject-inl.h
+++ b/js/src/vm/ScopeObject-inl.h
@@ -5,29 +5,20 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef vm_ScopeObject_inl_h
 #define vm_ScopeObject_inl_h
 
 #include "vm/ScopeObject.h"
 
 #include "jsinferinlines.h"
-#include "jsobjinlines.h"
 
 namespace js {
 
 inline void
-ScopeObject::setEnclosingScope(HandleObject obj)
-{
-    JS_ASSERT_IF(obj->is<CallObject>() || obj->is<DeclEnvObject>() || obj->is<BlockObject>(),
-                 obj->isDelegate());
-    setFixedSlot(SCOPE_CHAIN_SLOT, ObjectValue(*obj));
-}
-
-inline void
 ScopeObject::setAliasedVar(JSContext *cx, ScopeCoordinate sc, PropertyName *name, const Value &v)
 {
     JS_ASSERT(is<CallObject>() || is<ClonedBlockObject>());
     JS_STATIC_ASSERT(CallObject::RESERVED_SLOTS == BlockObject::RESERVED_SLOTS);
 
     // name may be null for non-singletons, whose types do not need to be tracked.
     JS_ASSERT_IF(hasSingletonType(), name);
 
@@ -40,68 +31,11 @@ inline void
 CallObject::setAliasedVar(JSContext *cx, AliasedFormalIter fi, PropertyName *name, const Value &v)
 {
     JS_ASSERT(name == fi->name());
     setSlot(fi.scopeSlot(), v);
     if (hasSingletonType())
         types::AddTypePropertyId(cx, this, NameToId(name), v);
 }
 
-inline void
-BlockObject::setSlotValue(unsigned i, const Value &v)
-{
-    setSlot(RESERVED_SLOTS + i, v);
-}
-
-inline void
-StaticBlockObject::initPrevBlockChainFromParser(StaticBlockObject *prev)
-{
-    setReservedSlot(SCOPE_CHAIN_SLOT, ObjectOrNullValue(prev));
-}
-
-inline void
-StaticBlockObject::resetPrevBlockChainFromParser()
-{
-    setReservedSlot(SCOPE_CHAIN_SLOT, UndefinedValue());
-}
-
-inline void
-StaticBlockObject::initEnclosingStaticScope(JSObject *obj)
-{
-    JS_ASSERT(getReservedSlot(SCOPE_CHAIN_SLOT).isUndefined());
-    setReservedSlot(SCOPE_CHAIN_SLOT, ObjectOrNullValue(obj));
-}
-
-inline void
-StaticBlockObject::setStackDepth(uint32_t depth)
-{
-    JS_ASSERT(getReservedSlot(DEPTH_SLOT).isUndefined());
-    initReservedSlot(DEPTH_SLOT, PrivateUint32Value(depth));
-}
-
-inline void
-StaticBlockObject::setDefinitionParseNode(unsigned i, frontend::Definition *def)
-{
-    JS_ASSERT(slotValue(i).isUndefined());
-    setSlotValue(i, PrivateValue(def));
-}
-
-inline void
-StaticBlockObject::setAliased(unsigned i, bool aliased)
-{
-    JS_ASSERT_IF(i > 0, slotValue(i-1).isBoolean());
-    setSlotValue(i, BooleanValue(aliased));
-    if (aliased && !needsClone()) {
-        setSlotValue(0, MagicValue(JS_BLOCK_NEEDS_CLONE));
-        JS_ASSERT(needsClone());
-    }
-}
-
-inline void
-ClonedBlockObject::setVar(unsigned i, const Value &v, MaybeCheckAliasing checkAliasing)
-{
-    JS_ASSERT_IF(checkAliasing, staticBlock().isAliased(i));
-    setSlotValue(i, v);
-}
-
 }  /* namespace js */
 
 #endif /* vm_ScopeObject_inl_h */
--- a/js/src/vm/ScopeObject.cpp
+++ b/js/src/vm/ScopeObject.cpp
@@ -161,16 +161,24 @@ js::ScopeCoordinateFunctionScript(JSCont
     }
     if (ssi.type() != StaticScopeIter::FUNCTION)
         return NULL;
     return ssi.funScript();
 }
 
 /*****************************************************************************/
 
+inline void
+ScopeObject::setEnclosingScope(HandleObject obj)
+{
+    JS_ASSERT_IF(obj->is<CallObject>() || obj->is<DeclEnvObject>() || obj->is<BlockObject>(),
+                 obj->isDelegate());
+    setFixedSlot(SCOPE_CHAIN_SLOT, ObjectValue(*obj));
+}
+
 /*
  * Construct a bare-bones call object given a shape, type, and slots pointer.
  * The call object must be further initialized to be usable.
  */
 CallObject *
 CallObject::create(JSContext *cx, HandleScript script, HandleShape shape, HandleTypeObject type, HeapSlot *slots)
 {
     gc::AllocKind kind = gc::GetGCObjectKind(shape->numFixedSlots());
--- a/js/src/vm/ScopeObject.h
+++ b/js/src/vm/ScopeObject.h
@@ -165,17 +165,18 @@ class ScopeObject : public JSObject
     /*
      * Since every scope chain terminates with a global object and GlobalObject
      * does not derive ScopeObject (it has a completely different layout), the
      * enclosing scope of a ScopeObject is necessarily non-null.
      */
     inline JSObject &enclosingScope() const {
         return getReservedSlot(SCOPE_CHAIN_SLOT).toObject();
     }
-    void setEnclosingScope(HandleObject obj);
+
+    inline void setEnclosingScope(HandleObject obj);
 
     /*
      * Get or set an aliased variable contained in this scope. Unaliased
      * variables should instead access the StackFrame. Aliased variable access
      * is primarily made through JOF_SCOPECOORD ops which is why these members
      * take a ScopeCoordinate instead of just the slot index.
      */
     inline const Value &aliasedVar(ScopeCoordinate sc);
@@ -337,17 +338,19 @@ class BlockObject : public NestedScopeOb
     }
 
   protected:
     /* Blocks contain an object slot for each slot i: 0 <= i < slotCount. */
     const Value &slotValue(unsigned i) {
         return getSlotRef(RESERVED_SLOTS + i);
     }
 
-    inline void setSlotValue(unsigned i, const Value &v);
+    void setSlotValue(unsigned i, const Value &v) {
+        setSlot(RESERVED_SLOTS + i, v);
+    }
 };
 
 class StaticBlockObject : public BlockObject
 {
   public:
     static StaticBlockObject *create(ExclusiveContext *cx);
 
     /* See StaticScopeIter comment. */
@@ -383,38 +386,62 @@ class StaticBlockObject : public BlockOb
      */
     bool needsClone() {
         return !slotValue(0).isFalse();
     }
 
     /* Frontend-only functions ***********************************************/
 
     /* Initialization functions for above fields. */
-    void setAliased(unsigned i, bool aliased);
-    void setStackDepth(uint32_t depth);
-    void initEnclosingStaticScope(JSObject *obj);
+    void setAliased(unsigned i, bool aliased) {
+        JS_ASSERT_IF(i > 0, slotValue(i-1).isBoolean());
+        setSlotValue(i, BooleanValue(aliased));
+        if (aliased && !needsClone()) {
+            setSlotValue(0, MagicValue(JS_BLOCK_NEEDS_CLONE));
+            JS_ASSERT(needsClone());
+        }
+    }
+
+    void setStackDepth(uint32_t depth) {
+        JS_ASSERT(getReservedSlot(DEPTH_SLOT).isUndefined());
+        initReservedSlot(DEPTH_SLOT, PrivateUint32Value(depth));
+    }
+
+    void initEnclosingStaticScope(JSObject *obj) {
+        JS_ASSERT(getReservedSlot(SCOPE_CHAIN_SLOT).isUndefined());
+        setReservedSlot(SCOPE_CHAIN_SLOT, ObjectOrNullValue(obj));
+    }
 
     /*
      * Frontend compilation temporarily uses the object's slots to link
      * a let var to its associated Definition parse node.
      */
-    void setDefinitionParseNode(unsigned i, frontend::Definition *def);
+    void setDefinitionParseNode(unsigned i, frontend::Definition *def) {
+        JS_ASSERT(slotValue(i).isUndefined());
+        setSlotValue(i, PrivateValue(def));
+    }
+
     frontend::Definition *maybeDefinitionParseNode(unsigned i) {
         Value v = slotValue(i);
         return v.isUndefined() ? NULL : reinterpret_cast<frontend::Definition *>(v.toPrivate());
     }
 
     /*
      * The parser uses 'enclosingBlock' as the prev-link in the pc->blockChain
      * stack. Note: in the case of hoisting, this prev-link will not ultimately
      * be the same as enclosingBlock, initEnclosingStaticScope must be called
      * separately in the emitter. 'reset' is just for asserting stackiness.
      */
-    void initPrevBlockChainFromParser(StaticBlockObject *prev);
-    void resetPrevBlockChainFromParser();
+    void initPrevBlockChainFromParser(StaticBlockObject *prev) {
+        setReservedSlot(SCOPE_CHAIN_SLOT, ObjectOrNullValue(prev));
+    }
+
+    void resetPrevBlockChainFromParser() {
+        setReservedSlot(SCOPE_CHAIN_SLOT, UndefinedValue());
+    }
 
     static Shape *addVar(ExclusiveContext *cx, Handle<StaticBlockObject*> block, HandleId id,
                          int index, bool *redeclared);
 };
 
 class ClonedBlockObject : public BlockObject
 {
   public:
@@ -427,17 +454,20 @@ class ClonedBlockObject : public BlockOb
     }
 
     /* Assuming 'put' has been called, return the value of the ith let var. */
     const Value &var(unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING) {
         JS_ASSERT_IF(checkAliasing, staticBlock().isAliased(i));
         return slotValue(i);
     }
 
-    void setVar(unsigned i, const Value &v, MaybeCheckAliasing checkAliasing = CHECK_ALIASING);
+    void setVar(unsigned i, const Value &v, MaybeCheckAliasing checkAliasing = CHECK_ALIASING) {
+        JS_ASSERT_IF(checkAliasing, staticBlock().isAliased(i));
+        setSlotValue(i, v);
+    }
 
     /* Copy in all the unaliased formals and locals. */
     void copyUnaliasedValues(AbstractFramePtr frame);
 };
 
 template<XDRMode mode>
 bool
 XDRStaticBlockObject(XDRState<mode> *xdr, HandleObject enclosingScope, HandleScript script,
--- a/js/src/vm/Stack-inl.h
+++ b/js/src/vm/Stack-inl.h
@@ -11,16 +11,18 @@
 
 #include "mozilla/PodOperations.h"
 
 #include "jscntxt.h"
 
 #include "jit/BaselineFrame.h"
 #include "vm/ScopeObject.h"
 
+#include "jsobjinlines.h"
+
 #include "jit/BaselineFrame-inl.h"
 #include "jit/IonFrameIterator-inl.h"
 
 namespace js {
 
 /*
  * We cache name lookup results only for the global object or for native
  * non-global objects without prototype or with prototype that never mutates,
--- a/js/src/vm/TypedArrayObject.cpp
+++ b/js/src/vm/TypedArrayObject.cpp
@@ -35,18 +35,16 @@
 #include "vm/Interpreter.h"
 #include "vm/NumericConversions.h"
 #include "vm/WrapperObject.h"
 
 #include "jsatominlines.h"
 #include "jsinferinlines.h"
 #include "jsobjinlines.h"
 
-#include "vm/GlobalObject-inl.h"
-
 #if JS_USE_NEW_OBJECT_REPRESENTATION
 // See the comment above OldObjectRepresentationHack.
 #  error "TypedArray support for new object representation unimplemented."
 #endif
 
 using namespace js;
 using namespace js::gc;
 using namespace js::types;
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -87,16 +87,17 @@
 
 #include <math.h>
 #include <stdarg.h>
 #include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
 
 #include "xpcpublic.h"
+#include "js/Tracer.h"
 #include "pldhash.h"
 #include "nscore.h"
 #include "nsXPCOM.h"
 #include "nsAutoPtr.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsDebug.h"
 #include "nsISupports.h"
 #include "nsIServiceManager.h"
--- a/layout/style/nsTransitionManager.cpp
+++ b/layout/style/nsTransitionManager.cpp
@@ -129,17 +129,18 @@ ElementPropertyTransition::IsRunningAt(T
          mStartTime <= aTime &&
          aTime < mStartTime + mDuration;
 }
 
 bool
 ElementTransitions::HasAnimationOfProperty(nsCSSProperty aProperty) const
 {
   for (uint32_t tranIdx = mPropertyTransitions.Length(); tranIdx-- != 0; ) {
-    if (aProperty == mPropertyTransitions[tranIdx].mProperty) {
+    const ElementPropertyTransition& pt = mPropertyTransitions[tranIdx];
+    if (aProperty == pt.mProperty && !pt.IsRemovedSentinel()) {
       return true;
     }
   }
   return false;
 }
 
 bool
 ElementTransitions::CanPerformOnCompositorThread(CanAnimateFlags aFlags) const
--- a/layout/style/test/test_transitions_per_property.html
+++ b/layout/style/test/test_transitions_per_property.html
@@ -581,16 +581,33 @@ for (prop in supported_properties) {
   // Test that prop is in the property database.
   ok(prop in gCSSProperties, "property " + prop + " in gCSSProperties");
 
   // Test that the entry has at least one test function.
   ok(supported_properties[prop].length > 0,
      "property " + prop + " must have at least one test function");
 }
 
+// Return a consistent sampling of |count| values out of |array|.
+function sample_array(array, count) {
+  if (count <= 0) {
+    ok(false, "unexpected count");
+    return [];
+  }
+  var ratio = array.length / count;
+  if (ratio <= 1) {
+    return array;
+  }
+  var result = new Array(count);
+  for (var i = 0; i < count; ++i) {
+    result[i] = array[Math.floor(i * ratio)];
+  }
+  return result;
+}
+
 // Test that transitions don't do anything (i.e., aren't supported) on
 // the properties not in our test list above (and not transition
 // properties themselves).
 for (prop in gCSSProperties) {
   var info = gCSSProperties[prop];
   if (!(prop in supported_properties) &&
       info.type != CSS_TYPE_TRUE_SHORTHAND &&
       !("alias_for" in info) &&
@@ -601,16 +618,36 @@ for (prop in gCSSProperties) {
     if ("prerequisites" in info) {
       var prereqs = info.prerequisites;
       for (var prereq in prereqs) {
         div.style.setProperty(prereq, prereqs[prereq], "");
       }
     }
 
     var all_values = info.initial_values.concat(info.other_values);
+
+    if (all_values.length > 50) {
+      // Since we're using an O(N^2) algorithm here, reduce the list of
+      // values that we want to test.  (This test is really only testing
+      // that somebody didn't make a property animatable without
+      // modifying this test.  The odds of somebody doing that without
+      // making at least one of the many pairs of values we have left
+      // animatable seems pretty low, at least relative to the chance
+      // that any pair of the values listed in property_database.js is
+      // animatable.)
+      //
+      // That said, we still try to use all of the start of the list on
+      // the assumption that the more basic values are likely to be at
+      // the beginning of the list.
+      all_values = [].concat(info.initial_values.slice(0,2),
+                             sample_array(info.initial_values.slice(2), 6),
+                             info.other_values.slice(0, 10),
+                             sample_array(info.other_values.slice(10), 40));
+    }
+
     var all_computed = [];
     for (var idx in all_values) {
       var val = all_values[idx];
       div.style.setProperty(prop, val, "");
       all_computed.push(cs.getPropertyValue(prop));
     }
     div.style.removeProperty(prop);
 
--- a/mobile/android/base/GeckoAppShell.java
+++ b/mobile/android/base/GeckoAppShell.java
@@ -6,16 +6,18 @@
 package org.mozilla.gecko;
 
 import org.mozilla.gecko.gfx.BitmapUtils;
 import org.mozilla.gecko.gfx.GeckoLayerClient;
 import org.mozilla.gecko.gfx.GfxInfoThread;
 import org.mozilla.gecko.gfx.LayerView;
 import org.mozilla.gecko.gfx.PanZoomController;
 import org.mozilla.gecko.mozglue.GeckoLoader;
+import org.mozilla.gecko.mozglue.GeneratableAndroidBridgeTarget;
+import org.mozilla.gecko.mozglue.OptionalGeneratedParameter;
 import org.mozilla.gecko.util.EventDispatcher;
 import org.mozilla.gecko.util.GeckoEventListener;
 import org.mozilla.gecko.util.HardwareUtils;
 import org.mozilla.gecko.util.ThreadUtils;
 
 import android.app.Activity;
 import android.app.ActivityManager;
 import android.app.PendingIntent;
@@ -366,30 +368,33 @@ public class GeckoAppShell
     }
 
     // Tell the Gecko event loop that an event is available.
     public static native void notifyGeckoOfEvent(GeckoEvent event);
 
     /*
      *  The Gecko-side API: API methods that Gecko calls
      */
+    @GeneratableAndroidBridgeTarget(generateStatic = true)
     public static void notifyIME(int type) {
         if (mEditableListener != null) {
             mEditableListener.notifyIME(type);
         }
     }
 
+    @GeneratableAndroidBridgeTarget(generateStatic = true)
     public static void notifyIMEContext(int state, String typeHint,
                                         String modeHint, String actionHint) {
         if (mEditableListener != null) {
             mEditableListener.notifyIMEContext(state, typeHint,
                                                modeHint, actionHint);
         }
     }
 
+    @GeneratableAndroidBridgeTarget(generateStatic = true)
     public static void notifyIMEChange(String text, int start, int end, int newEnd) {
         if (newEnd < 0) { // Selection change
             mEditableListener.onSelectionChange(start, end);
         } else { // Text change
             mEditableListener.onTextChange(text, start, end, newEnd);
         }
     }
 
@@ -423,16 +428,17 @@ public class GeckoAppShell
                 }
                 long waited = SystemClock.uptimeMillis() - time;
                 Log.d(LOGTAG, "Gecko event sync taking too long: " + waited + "ms");
             }
         }
     }
 
     // Signal the Java thread that it's time to wake up
+    @GeneratableAndroidBridgeTarget
     public static void acknowledgeEvent() {
         synchronized (sEventAckLock) {
             sWaitingForEventAck = false;
             sEventAckLock.notifyAll();
         }
     }
 
     private static float getLocationAccuracy(Location location) {
@@ -461,16 +467,17 @@ public class GeckoAppShell
                  getLocationAccuracy(location) < getLocationAccuracy(lastKnownLocation))) {
                 lastKnownLocation = location;
             }
         }
 
         return lastKnownLocation;
     }
 
+    @GeneratableAndroidBridgeTarget
     public static void enableLocation(final boolean enable) {
         ThreadUtils.postToUiThread(new Runnable() {
                 @Override
                 public void run() {
                     LocationManager lm = getLocationManager(getContext());
                     if (lm == null) {
                         return;
                     }
@@ -516,20 +523,22 @@ public class GeckoAppShell
             // which allows enabling/disabling location update notifications from the cell radio.
             // CONTROL_LOCATION_UPDATES is not for use by normal applications, but we might be
             // hitting this problem if the Tegras are confused about missing cell radios.
             Log.e(LOGTAG, "LOCATION_SERVICE not found?!", e);
             return null;
         }
     }
 
+    @GeneratableAndroidBridgeTarget
     public static void enableLocationHighAccuracy(final boolean enable) {
         mLocationHighAccuracy = enable;
     }
 
+    @GeneratableAndroidBridgeTarget
     public static void enableSensor(int aSensortype) {
         GeckoInterface gi = getGeckoInterface();
         if (gi == null)
             return;
         SensorManager sm = (SensorManager)
             getContext().getSystemService(Context.SENSOR_SERVICE);
 
         switch(aSensortype) {
@@ -574,16 +583,17 @@ public class GeckoAppShell
             if (gGyroscopeSensor != null)
                 sm.registerListener(gi.getSensorEventListener(), gGyroscopeSensor, sDefaultSensorHint);
             break;
         default:
             Log.w(LOGTAG, "Error! Can't enable unknown SENSOR type " + aSensortype);
         }
     }
 
+    @GeneratableAndroidBridgeTarget
     public static void disableSensor(int aSensortype) {
         GeckoInterface gi = getGeckoInterface();
         if (gi == null)
             return;
 
         SensorManager sm = (SensorManager)
             getContext().getSystemService(Context.SENSOR_SERVICE);
 
@@ -617,41 +627,44 @@ public class GeckoAppShell
             if (gGyroscopeSensor != null)
                 sm.unregisterListener(gi.getSensorEventListener(), gGyroscopeSensor);
             break;
         default:
             Log.w(LOGTAG, "Error! Can't disable unknown SENSOR type " + aSensortype);
         }
     }
 
+    @GeneratableAndroidBridgeTarget
     public static void moveTaskToBack() {
         if (getGeckoInterface() != null)
             getGeckoInterface().getActivity().moveTaskToBack(true);
     }
 
     public static void returnIMEQueryResult(String result, int selectionStart, int selectionLength) {
         // This method may be called from JNI to report Gecko's current selection indexes, but
         // Native Fennec doesn't care because the Java code already knows the selection indexes.
     }
 
+    @GeneratableAndroidBridgeTarget(stubName = "NotifyXreExit")
     static void onXreExit() {
         // The launch state can only be Launched or GeckoRunning at this point
         GeckoThread.setLaunchState(GeckoThread.LaunchState.GeckoExiting);
         if (getGeckoInterface() != null) {
             if (gRestartScheduled) {
                 getGeckoInterface().doRestart();
             } else {
                 getGeckoInterface().getActivity().finish();
             }
         }
 
         Log.d(LOGTAG, "Killing via System.exit()");
         System.exit(0);
     }
 
+    @GeneratableAndroidBridgeTarget
     static void scheduleRestart() {
         gRestartScheduled = true;
     }
 
     public static File preInstallWebApp(String aTitle, String aURI, String aOrigin) {
         int index = WebAppAllocator.getInstance(getContext()).findAndAllocateIndex(aOrigin, aTitle, (String) null);
         GeckoProfile profile = GeckoProfile.get(getContext(), "webapp" + index);
         return profile.getDir();
@@ -684,16 +697,17 @@ public class GeckoAppShell
         intent.setData(Uri.parse(aURI));
         intent.setClassName(AppConstants.ANDROID_PACKAGE_NAME,
                             AppConstants.ANDROID_PACKAGE_NAME + ".WebApps$WebApp" + aIndex);
         return intent;
     }
 
     // "Installs" an application by creating a shortcut
     // This is the entry point from AndroidBridge.h
+    @GeneratableAndroidBridgeTarget
     static void createShortcut(String aTitle, String aURI, String aIconData, String aType) {
         if ("webapp".equals(aType)) {
             Log.w(LOGTAG, "createShortcut with no unique URI should not be used for aType = webapp!");
         }
 
         createShortcut(aTitle, aURI, aURI, aIconData, aType);
     }
 
@@ -878,23 +892,25 @@ public class GeckoAppShell
                                    halfSize - sHeight,
                                    halfSize + sWidth,
                                    halfSize + sHeight),
                           null);
 
         return bitmap;
     }
 
+    @GeneratableAndroidBridgeTarget(stubName = "GetHandlersForMimeTypeWrapper")
     static String[] getHandlersForMimeType(String aMimeType, String aAction) {
         Intent intent = getIntentForActionString(aAction);
         if (aMimeType != null && aMimeType.length() > 0)
             intent.setType(aMimeType);
         return getHandlersForIntent(intent);
     }
 
+    @GeneratableAndroidBridgeTarget(stubName = "GetHandlersForURLWrapper")
     static String[] getHandlersForURL(String aURL, String aAction) {
         // aURL may contain the whole URL or just the protocol
         Uri uri = aURL.indexOf(':') >= 0 ? Uri.parse(aURL) : new Uri.Builder().scheme(aURL).build();
 
         Intent intent = getOpenURIIntent(getContext(), uri.toString(), "",
             TextUtils.isEmpty(aAction) ? Intent.ACTION_VIEW : aAction, "");
 
         return getHandlersForIntent(intent);
@@ -927,20 +943,22 @@ public class GeckoAppShell
     static Intent getIntentForActionString(String aAction) {
         // Default to the view action if no other action as been specified.
         if (TextUtils.isEmpty(aAction)) {
             return new Intent(Intent.ACTION_VIEW);
         }
         return new Intent(aAction);
     }
 
+    @GeneratableAndroidBridgeTarget(stubName = "GetExtensionFromMimeTypeWrapper")
     static String getExtensionFromMimeType(String aMimeType) {
         return MimeTypeMap.getSingleton().getExtensionFromMimeType(aMimeType);
     }
 
+    @GeneratableAndroidBridgeTarget(stubName = "GetMimeTypeFromExtensionsWrapper")
     static String getMimeTypeFromExtensions(String aFileExt) {
         MimeTypeMap mtm = MimeTypeMap.getSingleton();
         StringTokenizer st = new StringTokenizer(aFileExt, ".,; ");
         String type = null;
         String subType = null;
         while (st.hasMoreElements()) {
             String ext = st.nextToken();
             String mt = mtm.getMimeTypeFromExtension(ext);
@@ -1064,22 +1082,23 @@ public class GeckoAppShell
      *
      * @param targetURI the string spec of the URI to open.
      * @param mimeType an optional MIME type string.
      * @param action an Android action specifier, such as
      *               <code>Intent.ACTION_SEND</code>.
      * @param title the title to use in <code>ACTION_SEND</code> intents.
      * @return true if the activity started successfully; false otherwise.
      */
+    @GeneratableAndroidBridgeTarget
     public static boolean openUriExternal(String targetURI,
                                           String mimeType,
-                                          String packageName,
-                                          String className,
-                                          String action,
-                                          String title) {
+              @OptionalGeneratedParameter String packageName,
+              @OptionalGeneratedParameter String className,
+              @OptionalGeneratedParameter String action,
+              @OptionalGeneratedParameter String title) {
         final Context context = getContext();
         final Intent intent = getOpenURIIntent(context, targetURI,
                                                mimeType, action, title);
 
         if (intent == null) {
             return false;
         }
 
@@ -1249,16 +1268,17 @@ public class GeckoAppShell
     public static void setNotificationClient(NotificationClient client) {
         if (sNotificationClient == null) {
             sNotificationClient = client;
         } else {
             Log.d(LOGTAG, "Notification client already set");
         }
     }
 
+    @GeneratableAndroidBridgeTarget(stubName = "ShowAlertNotificationWrapper")
     public static void showAlertNotification(String aImageUrl, String aAlertTitle, String aAlertText,
                                              String aAlertCookie, String aAlertName) {
         // The intent to launch when the user clicks the expanded notification
         String app = getContext().getClass().getName();
         Intent notificationIntent = new Intent(GeckoApp.ACTION_ALERT_CALLBACK);
         notificationIntent.setClassName(AppConstants.ANDROID_PACKAGE_NAME, app);
         notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 
@@ -1275,21 +1295,23 @@ public class GeckoAppShell
                 getContext(), 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
 
         mAlertCookies.put(aAlertName, aAlertCookie);
         callObserver(aAlertName, "alertshow", aAlertCookie);
 
         sNotificationClient.add(notificationID, aImageUrl, aAlertTitle, aAlertText, contentIntent);
     }
 
+    @GeneratableAndroidBridgeTarget
     public static void alertsProgressListener_OnProgress(String aAlertName, long aProgress, long aProgressMax, String aAlertText) {
         int notificationID = aAlertName.hashCode();
         sNotificationClient.update(notificationID, aProgress, aProgressMax, aAlertText);
     }
 
+    @GeneratableAndroidBridgeTarget
     public static void closeNotification(String aAlertName) {
         String alertCookie = mAlertCookies.get(aAlertName);
         if (alertCookie != null) {
             callObserver(aAlertName, "alertfinished", alertCookie);
             mAlertCookies.remove(aAlertName);
         }
 
         removeObserver(aAlertName);
@@ -1313,16 +1335,17 @@ public class GeckoAppShell
         // Also send a notification to the observer service
         // New listeners should register for these notifications since they will be called even if
         // Gecko has been killed and restared between when your notification was shown and when the
         // user clicked on it.
         sendEventToGecko(GeckoEvent.createBroadcastEvent("Notification:Clicked", aAlertCookie));
         closeNotification(aAlertName);
     }
 
+    @GeneratableAndroidBridgeTarget(stubName = "GetDpiWrapper")
     public static int getDpi() {
         if (sDensityDpi == 0) {
             sDensityDpi = getContext().getResources().getDisplayMetrics().densityDpi;
         }
 
         return sDensityDpi;
     }
 
@@ -1351,16 +1374,17 @@ public class GeckoAppShell
         }
         return false;
     }
 
     /**
      * Returns the colour depth of the default screen. This will either be
      * 24 or 16.
      */
+    @GeneratableAndroidBridgeTarget(stubName = "GetScreenDepthWrapper")
     public static synchronized int getScreenDepth() {
         if (sScreenDepth == 0) {
             sScreenDepth = 16;
             PixelFormat info = new PixelFormat();
             PixelFormat.getPixelFormatInfo(getGeckoInterface().getActivity().getWindowManager().getDefaultDisplay().getPixelFormat(), info);
             if (info.bitsPerPixel >= 24 && isHighMemoryDevice()) {
                 sScreenDepth = 24;
             }
@@ -1373,120 +1397,133 @@ public class GeckoAppShell
         if (sScreenDepth != 0) {
             Log.e(LOGTAG, "Tried to override screen depth after it's already been set");
             return;
         }
 
         sScreenDepth = aScreenDepth;
     }
 
+    @GeneratableAndroidBridgeTarget
     public static void setFullScreen(boolean fullscreen) {
         if (getGeckoInterface() != null)
             getGeckoInterface().setFullScreen(fullscreen);
     }
 
+    @GeneratableAndroidBridgeTarget(stubName = "ShowFilePickerForExtensionsWrapper")
     public static String showFilePickerForExtensions(String aExtensions) {
         if (getGeckoInterface() != null)
             return sActivityHelper.showFilePicker(getGeckoInterface().getActivity(), getMimeTypeFromExtensions(aExtensions));
         return "";
     }
 
+    @GeneratableAndroidBridgeTarget(stubName = "ShowFilePickerForMimeTypeWrapper")
     public static String showFilePickerForMimeType(String aMimeType) {
         if (getGeckoInterface() != null)
             return sActivityHelper.showFilePicker(getGeckoInterface().getActivity(), aMimeType);
         return "";
     }
 
+    @GeneratableAndroidBridgeTarget
     public static void performHapticFeedback(boolean aIsLongPress) {
         // Don't perform haptic feedback if a vibration is currently playing,
         // because the haptic feedback will nuke the vibration.
         if (!sVibrationMaybePlaying || System.nanoTime() >= sVibrationEndTime) {
             LayerView layerView = getLayerView();
             layerView.performHapticFeedback(aIsLongPress ?
                                             HapticFeedbackConstants.LONG_PRESS :
                                             HapticFeedbackConstants.VIRTUAL_KEY);
         }
     }
 
     private static Vibrator vibrator() {
         LayerView layerView = getLayerView();
         return (Vibrator) layerView.getContext().getSystemService(Context.VIBRATOR_SERVICE);
     }
 
+    @GeneratableAndroidBridgeTarget(stubName = "Vibrate1")
     public static void vibrate(long milliseconds) {
         sVibrationEndTime = System.nanoTime() + milliseconds * 1000000;
         sVibrationMaybePlaying = true;
         vibrator().vibrate(milliseconds);
     }
 
+    @GeneratableAndroidBridgeTarget(stubName = "VibrateA")
     public static void vibrate(long[] pattern, int repeat) {
         // If pattern.length is even, the last element in the pattern is a
         // meaningless delay, so don't include it in vibrationDuration.
         long vibrationDuration = 0;
         int iterLen = pattern.length - (pattern.length % 2 == 0 ? 1 : 0);
         for (int i = 0; i < iterLen; i++) {
           vibrationDuration += pattern[i];
         }
 
         sVibrationEndTime = System.nanoTime() + vibrationDuration * 1000000;
         sVibrationMaybePlaying = true;
         vibrator().vibrate(pattern, repeat);
     }
 
+    @GeneratableAndroidBridgeTarget
     public static void cancelVibrate() {
         sVibrationMaybePlaying = false;
         sVibrationEndTime = 0;
         vibrator().cancel();
     }
 
+    @GeneratableAndroidBridgeTarget
     public static void showInputMethodPicker() {
         InputMethodManager imm = (InputMethodManager)
             getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
         imm.showInputMethodPicker();
     }
 
+    @GeneratableAndroidBridgeTarget
     public static void setKeepScreenOn(final boolean on) {
         ThreadUtils.postToUiThread(new Runnable() {
             @Override
             public void run() {
                 // TODO
             }
         });
     }
 
+    @GeneratableAndroidBridgeTarget
     public static void notifyDefaultPrevented(final boolean defaultPrevented) {
         ThreadUtils.postToUiThread(new Runnable() {
             @Override
             public void run() {
                 LayerView view = getLayerView();
                 PanZoomController controller = (view == null ? null : view.getPanZoomController());
                 if (controller != null) {
                     controller.notifyDefaultActionPrevented(defaultPrevented);
                 }
             }
         });
     }
 
+    @GeneratableAndroidBridgeTarget
     public static boolean isNetworkLinkUp() {
         ConnectivityManager cm = (ConnectivityManager)
            getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
         NetworkInfo info = cm.getActiveNetworkInfo();
         if (info == null || !info.isConnected())
             return false;
         return true;
     }
 
+    @GeneratableAndroidBridgeTarget
     public static boolean isNetworkLinkKnown() {
         ConnectivityManager cm = (ConnectivityManager)
             getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
         if (cm.getActiveNetworkInfo() == null)
             return false;
         return true;
     }
 
+    @GeneratableAndroidBridgeTarget
     public static void setSelectedLocale(String localeCode) {
         /* Bug 713464: This method is still called from Gecko side.
            Earlier we had an option to run Firefox in a language other than system's language.
            However, this is not supported as of now.
            Gecko resets the locale to en-US by calling this function with an empty string.
            This affects GeckoPreferences activity in multi-locale builds.
 
         //We're not using this, not need to save it (see bug 635342)
@@ -1508,16 +1545,18 @@ public class GeckoAppShell
 
         Resources res = getContext().getBaseContext().getResources();
         Configuration config = res.getConfiguration();
         config.locale = locale;
         res.updateConfiguration(config, res.getDisplayMetrics());
         */
     }
 
+
+    @GeneratableAndroidBridgeTarget(stubName = "GetSystemColoursWrapper")
     public static int[] getSystemColors() {
         // attrsAppearance[] must correspond to AndroidSystemColors structure in android/AndroidBridge.h
         final int[] attrsAppearance = {
             android.R.attr.textColor,
             android.R.attr.textColorPrimary,
             android.R.attr.textColorPrimaryInverse,
             android.R.attr.textColorSecondary,
             android.R.attr.textColorSecondaryInverse,
@@ -1544,16 +1583,17 @@ public class GeckoAppShell
                 result[idx] = color;
             }
             appearance.recycle();
         }
 
         return result;
     }
 
+    @GeneratableAndroidBridgeTarget
     public static void killAnyZombies() {
         GeckoProcessesVisitor visitor = new GeckoProcessesVisitor() {
             @Override
             public boolean callback(int pid) {
                 if (pid != android.os.Process.myPid())
                     android.os.Process.killProcess(pid);
                 return true;
             }
@@ -1699,21 +1739,23 @@ public class GeckoAppShell
                 String file = split[nameColumn];
                 if (!TextUtils.isEmpty(name) && !TextUtils.isEmpty(file) && file.startsWith(filter))
                     Log.i(LOGTAG, "[OPENFILE] " + name + "(" + split[pidColumn] + ") : " + file);
             }
             in.close();
         } catch (Exception e) { }
     }
 
+    @GeneratableAndroidBridgeTarget
     public static void scanMedia(String aFile, String aMimeType) {
         Context context = getContext();
         GeckoMediaScannerClient.startScan(context, aFile, aMimeType);
     }
 
+    @GeneratableAndroidBridgeTarget(stubName = "GetIconForExtensionWrapper")
     public static byte[] getIconForExtension(String aExt, int iconSize) {
         try {
             if (iconSize <= 0)
                 iconSize = 16;
 
             if (aExt != null && aExt.length() > 1 && aExt.charAt(0) == '.')
                 aExt = aExt.substring(1);
 
@@ -1757,36 +1799,39 @@ public class GeckoAppShell
         if (resolveInfo == null)
             return null;
 
         ActivityInfo activityInfo = resolveInfo.activityInfo;
 
         return activityInfo.loadIcon(pm);
     }
 
+    @GeneratableAndroidBridgeTarget
     public static boolean getShowPasswordSetting() {
         try {
             int showPassword =
                 Settings.System.getInt(getContext().getContentResolver(),
                                        Settings.System.TEXT_SHOW_PASSWORD, 1);
             return (showPassword > 0);
         }
         catch (Exception e) {
             return true;
         }
     }
 
+    @GeneratableAndroidBridgeTarget(stubName = "AddPluginViewWrapper")
     public static void addPluginView(View view,
                                      float x, float y,
                                      float w, float h,
                                      boolean isFullScreen) {
         if (getGeckoInterface() != null)
              getGeckoInterface().addPluginView(view, new RectF(x, y, x + w, y + h), isFullScreen);
     }
 
+    @GeneratableAndroidBridgeTarget
     public static void removePluginView(View view, boolean isFullScreen) {
         if (getGeckoInterface() != null)
             getGeckoInterface().removePluginView(view, isFullScreen);
     }
 
     /**
      * A plugin that wish to be loaded in the WebView must provide this permission
      * in their AndroidManifest.xml.
@@ -2000,16 +2045,17 @@ public class GeckoAppShell
         } catch (android.content.pm.PackageManager.NameNotFoundException nnfe) {
             Log.w(LOGTAG, "Couldn't find package.", nnfe);
             return null;
         }
     }
 
     private static ContextGetter sContextGetter;
 
+    @GeneratableAndroidBridgeTarget(allowMultithread = true)
     public static Context getContext() {
         return sContextGetter.getContext();
     }
 
     public static void setContextGetter(ContextGetter cg) {
         sContextGetter = cg;
     }
 
@@ -2056,16 +2102,18 @@ public class GeckoAppShell
 
     public static android.hardware.Camera sCamera = null;
 
     static native void cameraCallbackBridge(byte[] data);
 
     static int kPreferedFps = 25;
     static byte[] sCameraBuffer = null;
 
+
+    @GeneratableAndroidBridgeTarget(stubName = "InitCameraWrapper")
     static int[] initCamera(String aContentType, int aCamera, int aWidth, int aHeight) {
         ThreadUtils.postToUiThread(new Runnable() {
                 @Override
                 public void run() {
                     try {
                         if (getGeckoInterface() != null)
                             getGeckoInterface().enableCameraView();
                     } catch (Exception e) {}
@@ -2156,16 +2204,17 @@ public class GeckoAppShell
             result[3] = params.getPreviewFrameRate();
         } catch(RuntimeException e) {
             Log.w(LOGTAG, "initCamera RuntimeException.", e);
             result[0] = result[1] = result[2] = result[3] = 0;
         }
         return result;
     }
 
+    @GeneratableAndroidBridgeTarget
     static synchronized void closeCamera() {
         ThreadUtils.postToUiThread(new Runnable() {
                 @Override
                 public void run() {
                     try {
                         if (getGeckoInterface() != null)
                             getGeckoInterface().disableCameraView();
                     } catch (Exception e) {}
@@ -2207,130 +2256,148 @@ public class GeckoAppShell
      */
     public static void unregisterEventListener(String event, GeckoEventListener listener) {
         sEventDispatcher.unregisterEventListener(event, listener);
     }
 
     /*
      * Battery API related methods.
      */
+    @GeneratableAndroidBridgeTarget
     public static void enableBatteryNotifications() {
         GeckoBatteryManager.enableNotifications();
     }
 
+    @GeneratableAndroidBridgeTarget(stubName = "HandleGeckoMessageWrapper")
     public static String handleGeckoMessage(String message) {
         return sEventDispatcher.dispatchEvent(message);
     }
 
+    @GeneratableAndroidBridgeTarget
     public static void disableBatteryNotifications() {
         GeckoBatteryManager.disableNotifications();
     }
 
+    @GeneratableAndroidBridgeTarget(stubName = "GetCurrentBatteryInformationWrapper")
     public static double[] getCurrentBatteryInformation() {
         return GeckoBatteryManager.getCurrentInformation();
     }
 
-    static void checkUriVisited(String uri) {   // invoked from native JNI code
+    @GeneratableAndroidBridgeTarget(stubName = "CheckURIVisited")
+    static void checkUriVisited(String uri) {
         GlobalHistory.getInstance().checkUriVisited(uri);
     }
 
-    static void markUriVisited(final String uri) {    // invoked from native JNI code
+    @GeneratableAndroidBridgeTarget(stubName = "MarkURIVisited")
+    static void markUriVisited(final String uri) {
         ThreadUtils.postToBackgroundThread(new Runnable() {
             @Override
             public void run() {
                 GlobalHistory.getInstance().add(uri);
             }
         });
     }
 
-    static void setUriTitle(final String uri, final String title) {    // invoked from native JNI code
+    @GeneratableAndroidBridgeTarget(stubName = "SetURITitle")
+    static void setUriTitle(final String uri, final String title) {
         ThreadUtils.postToBackgroundThread(new Runnable() {
             @Override
             public void run() {
                 GlobalHistory.getInstance().update(uri, title);
             }
         });
     }
 
+    @GeneratableAndroidBridgeTarget
     static void hideProgressDialog() {
         // unused stub
     }
 
     /*
      * WebSMS related methods.
      */
+    @GeneratableAndroidBridgeTarget(stubName = "SendMessageWrapper")
     public static void sendMessage(String aNumber, String aMessage, int aRequestId) {
         if (SmsManager.getInstance() == null) {
             return;
         }
 
         SmsManager.getInstance().send(aNumber, aMessage, aRequestId);
     }
 
+    @GeneratableAndroidBridgeTarget(stubName = "GetMessageWrapper")
     public static void getMessage(int aMessageId, int aRequestId) {
         if (SmsManager.getInstance() == null) {
             return;
         }
 
         SmsManager.getInstance().getMessage(aMessageId, aRequestId);
     }
 
+    @GeneratableAndroidBridgeTarget(stubName = "DeleteMessageWrapper")
     public static void deleteMessage(int aMessageId, int aRequestId) {
         if (SmsManager.getInstance() == null) {
             return;
         }
 
         SmsManager.getInstance().deleteMessage(aMessageId, aRequestId);
     }
 
+    @GeneratableAndroidBridgeTarget(stubName = "CreateMessageListWrapper")
     public static void createMessageList(long aStartDate, long aEndDate, String[] aNumbers, int aNumbersCount, int aDeliveryState, boolean aReverse, int aRequestId) {
         if (SmsManager.getInstance() == null) {
             return;
         }
 
         SmsManager.getInstance().createMessageList(aStartDate, aEndDate, aNumbers, aNumbersCount, aDeliveryState, aReverse, aRequestId);
     }
 
+    @GeneratableAndroidBridgeTarget(stubName = "GetNextMessageInListWrapper")
     public static void getNextMessageInList(int aListId, int aRequestId) {
         if (SmsManager.getInstance() == null) {
             return;
         }
 
         SmsManager.getInstance().getNextMessageInList(aListId, aRequestId);
     }
 
+    @GeneratableAndroidBridgeTarget
     public static void clearMessageList(int aListId) {
         if (SmsManager.getInstance() == null) {
             return;
         }
 
         SmsManager.getInstance().clearMessageList(aListId);
     }
 
     /* Called by JNI from AndroidBridge, and by reflection from tests/BaseTest.java.in */
+    @GeneratableAndroidBridgeTarget
     public static boolean isTablet() {
         return HardwareUtils.isTablet();
     }
 
     public static void viewSizeChanged() {
         LayerView v = getLayerView();
         if (v != null && v.isIMEEnabled()) {
             sendEventToGecko(GeckoEvent.createBroadcastEvent(
                     "ScrollTo:FocusedInput", ""));
         }
     }
 
+    @GeneratableAndroidBridgeTarget(stubName = "GetCurrentNetworkInformationWrapper")
     public static double[] getCurrentNetworkInformation() {
         return GeckoNetworkManager.getInstance().getCurrentInformation();
     }
 
+    @GeneratableAndroidBridgeTarget
     public static void enableNetworkNotifications() {
         GeckoNetworkManager.getInstance().enableNotifications();
     }
 
+    @GeneratableAndroidBridgeTarget
     public static void disableNetworkNotifications() {
         GeckoNetworkManager.getInstance().disableNotifications();
     }
 
     // values taken from android's Base64
     public static final int BASE64_DEFAULT = 0;
     public static final int BASE64_URL_SAFE = 8;
 
@@ -2434,36 +2501,42 @@ public class GeckoAppShell
             if (op<oLen) out[op++] = (byte)o2; }
         return out; 
     }
 
     public static byte[] decodeBase64(String s, int flags) {
         return decodeBase64(s.getBytes(), flags);
     }
 
+    @GeneratableAndroidBridgeTarget(stubName = "GetScreenOrientationWrapper")
     public static short getScreenOrientation() {
         return GeckoScreenOrientationListener.getInstance().getScreenOrientation();
     }
 
+    @GeneratableAndroidBridgeTarget
     public static void enableScreenOrientationNotifications() {
         GeckoScreenOrientationListener.getInstance().enableNotifications();
     }
 
+    @GeneratableAndroidBridgeTarget
     public static void disableScreenOrientationNotifications() {
         GeckoScreenOrientationListener.getInstance().disableNotifications();
     }
 
+    @GeneratableAndroidBridgeTarget
     public static void lockScreenOrientation(int aOrientation) {
         GeckoScreenOrientationListener.getInstance().lockScreenOrientation(aOrientation);
     }
 
+    @GeneratableAndroidBridgeTarget
     public static void unlockScreenOrientation() {
         GeckoScreenOrientationListener.getInstance().unlockScreenOrientation();
     }
 
+    @GeneratableAndroidBridgeTarget
     public static boolean pumpMessageLoop() {
         Handler geckoHandler = ThreadUtils.sGeckoHandler;
         Message msg = getNextMessageFromQueue(ThreadUtils.sGeckoQueue);
 
         if (msg == null)
             return false;
         if (msg.obj == geckoHandler && msg.getTarget() == geckoHandler) {
             // Our "queue is empty" message; see runGecko()
@@ -2475,59 +2548,66 @@ public class GeckoAppShell
         else
             msg.getTarget().dispatchMessage(msg);
         msg.recycle();
         return true;
     }
 
     static native void notifyFilePickerResult(String filePath, long id);
 
+    @GeneratableAndroidBridgeTarget(stubName = "ShowFilePickerAsyncWrapper")
     public static void showFilePickerAsync(String aMimeType, final long id) {
         sActivityHelper.showFilePickerAsync(getGeckoInterface().getActivity(), aMimeType, new ActivityHandlerHelper.FileResultHandler() {
             public void gotFile(String filename) {
                 GeckoAppShell.notifyFilePickerResult(filename, id);
             }
         });
     }
 
+    @GeneratableAndroidBridgeTarget
     public static void notifyWakeLockChanged(String topic, String state) {
         if (getGeckoInterface() != null)
             getGeckoInterface().notifyWakeLockChanged(topic, state);
     }
 
+    @GeneratableAndroidBridgeTarget(stubName = "GetGfxInfoDataWrapper")
     public static String getGfxInfoData() {
         return GfxInfoThread.getData();
     }
 
+    @GeneratableAndroidBridgeTarget
     public static void registerSurfaceTextureFrameListener(Object surfaceTexture, final int id) {
         ((SurfaceTexture)surfaceTexture).setOnFrameAvailableListener(new SurfaceTexture.OnFrameAvailableListener() {
             @Override
             public void onFrameAvailable(SurfaceTexture surfaceTexture) {
                 GeckoAppShell.onSurfaceTextureFrameAvailable(surfaceTexture, id);
             }
         });
     }
 
+    @GeneratableAndroidBridgeTarget(allowMultithread = true)
     public static void unregisterSurfaceTextureFrameListener(Object surfaceTexture) {
         ((SurfaceTexture)surfaceTexture).setOnFrameAvailableListener(null);
     }
 
+    @GeneratableAndroidBridgeTarget
     public static boolean unlockProfile() {
         // Try to kill any zombie Fennec's that might be running
         GeckoAppShell.killAnyZombies();
 
         // Then force unlock this profile
         if (getGeckoInterface() != null) {
             GeckoProfile profile = getGeckoInterface().getProfile();
             File lock = profile.getFile(".parentlock");
             return lock.exists() && lock.delete();
         }
         return false;
     }
 
+    @GeneratableAndroidBridgeTarget(stubName = "GetProxyForURIWrapper")
     public static String getProxyForURI(String spec, String scheme, String host, int port) {
         URI uri = null;
         try {
             uri = new URI(spec);
         } catch(java.net.URISyntaxException uriEx) {
             try {
                 uri = new URI(scheme, null, host, port, null, null, null);
             } catch(java.net.URISyntaxException uriEx2) {
--- a/mobile/android/base/GeckoJavaSampler.java
+++ b/mobile/android/base/GeckoJavaSampler.java
@@ -2,16 +2,19 @@
  * 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/. */
 
 package org.mozilla.gecko;
 
 import android.os.SystemClock;
 import android.util.Log;
+
+import org.mozilla.gecko.mozglue.GeneratableAndroidBridgeTarget;
+
 import java.lang.Thread;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Set;
 
 public class GeckoJavaSampler {
     private static final String LOGTAG = "JavaSampler";
     private static Thread sSamplingThread = null;
@@ -117,74 +120,83 @@ public class GeckoJavaSampler {
                 }
                 int readPos = (startPos + aSampleId) % mSamples.get(aThreadId).length;
                 return mSamples.get(aThreadId)[readPos];
             }
             return null;
         }
     }
 
+
+    @GeneratableAndroidBridgeTarget(allowMultithread = true, stubName = "GetThreadNameJavaProfilingWrapper")
     public synchronized static String getThreadName(int aThreadId) {
         if (aThreadId == 0 && sMainThread != null) {
             return sMainThread.getName();
         }
         return null;
     }
 
     private synchronized static Sample getSample(int aThreadId, int aSampleId) {
         return sSamplingRunnable.getSample(aThreadId, aSampleId);
     }
 
+    @GeneratableAndroidBridgeTarget(allowMultithread = true, stubName = "GetSampleTimeJavaProfiling")
     public synchronized static double getSampleTime(int aThreadId, int aSampleId) {
         Sample sample = getSample(aThreadId, aSampleId);
         if (sample != null) {
             if (sample.mJavaTime != 0) {
                 return (double)(sample.mJavaTime -
                     SystemClock.elapsedRealtime()) + getProfilerTime();
             }
             System.out.println("Sample: " + sample.mTime);
             return sample.mTime;
         }
         return 0;
     }
+
+    @GeneratableAndroidBridgeTarget(allowMultithread = true, stubName = "GetFrameNameJavaProfilingWrapper")
     public synchronized static String getFrameName(int aThreadId, int aSampleId, int aFrameId) {
         Sample sample = getSample(aThreadId, aSampleId);
         if (sample != null && aFrameId < sample.mFrames.length) {
             Frame frame = sample.mFrames[aFrameId];
             if (frame == null) {
                 return null;
             }
             return frame.className + "." + frame.methodName + "()";
         }
         return null;
     }
 
+    @GeneratableAndroidBridgeTarget(allowMultithread = true, stubName = "StartJavaProfiling")
     public static void start(int aInterval, int aSamples) {
         synchronized (GeckoJavaSampler.class) {
             if (sSamplingRunnable != null) {
                 return;
             }
             sSamplingRunnable = new SamplingThread(aInterval, aSamples);
             sSamplingThread = new Thread(sSamplingRunnable, "Java Sampler");
             sSamplingThread.start();
         }
     }
 
+    @GeneratableAndroidBridgeTarget(allowMultithread = true, stubName = "PauseJavaProfiling")
     public static void pause() {
         synchronized (GeckoJavaSampler.class) {
             sSamplingRunnable.mPauseSampler = true;
         }
     }
 
+    @GeneratableAndroidBridgeTarget(allowMultithread = true, stubName = "UnpauseJavaProfiling")
     public static void unpause() {
         synchronized (GeckoJavaSampler.class) {
             sSamplingRunnable.mPauseSampler = false;
         }
     }
 
+    @GeneratableAndroidBridgeTarget(allowMultithread = true, stubName = "StopJavaProfiling")
     public static void stop() {
         synchronized (GeckoJavaSampler.class) {
             if (sSamplingThread == null) {
                 return;
             }
 
             sSamplingRunnable.mStopSampler = true;
             try {
--- a/mobile/android/base/GeckoSmsManager.java
+++ b/mobile/android/base/GeckoSmsManager.java
@@ -298,17 +298,17 @@ public class GeckoSmsManager
   public final static int kNoError             = 0;
   public final static int kNoSignalError       = 1;
   public final static int kNotFoundError       = 2;
   public final static int kUnknownError        = 3;
   public final static int kInternalError       = 4;
   public final static int kNoSimCardError      = 5;
   public final static int kRadioDisabledError  = 6;
   public final static int kInvalidAddressError = 7;
-
+  public final static int kFdnCheckError       = 8;
 
   private final static int kMaxMessageSize    = 160;
 
   private final static Uri kSmsContentUri     = Uri.parse("content://sms");
   private final static Uri kSmsSentContentUri = Uri.parse("content://sms/sent");
 
   private final static int kSmsTypeInbox      = 1;
   private final static int kSmsTypeSentbox    = 2;
--- a/mobile/android/base/Makefile.in
+++ b/mobile/android/base/Makefile.in
@@ -4,18 +4,20 @@
 
 DIST_FILES = package-name.txt
 
 include $(topsrcdir)/mobile/android/base/android-services-files.mk
 
 MOZGLUE_JAVA_FILES := \
   mozglue/ByteBufferInputStream.java \
   mozglue/DirectBufferAllocator.java \
+  mozglue/GeneratableAndroidBridgeTarget.java \
   mozglue/NativeReference.java \
   mozglue/NativeZip.java \
+  mozglue/OptionalGeneratedParameter.java \
   $(NULL)
 
 MOZGLUE_PP_JAVA_FILES := \
   mozglue/GeckoLoader.java \
   $(NULL)
 
 UTIL_JAVA_FILES := \
   util/ActivityResultHandler.java \
@@ -400,16 +402,18 @@ GARBAGE += \
   R.java \
   $(FENNEC_PP_XML_FILES) \
   $(SYNC_PP_RES_XML) \
   package-name.txt \
   fennec_ids.txt \
   Manifest.java \
   javah.out \
   jni-stubs.inc \
+  GeneratedJNIWrappers.cpp \
+  GeneratedJNIWrappers.h \
   $(NULL)
 
 GARBAGE_DIRS += classes db jars res sync services
 
 MOZ_ANDROID_SHARED_ID = "$(ANDROID_PACKAGE_NAME).sharedID"
 MOZ_ANDROID_SHARED_ACCOUNT_TYPE = "$(ANDROID_PACKAGE_NAME)_sync"
 
 # Bug 567884 - Need a way to find appropriate icons during packaging
@@ -1259,20 +1263,20 @@ jars/sync-thirdparty.jar: $(addprefix $(
 
 jars/websockets.jar: $(addprefix $(srcdir)/, $(WEBSOCKETS_JAVA_FILES)) jars
 	@echo "JAR websockets.jar"
 	$(NSINSTALL) -D classes/websockets
 	$(JAVAC) $(JAVAC_FLAGS) -Xlint:all,-serial -d classes/websockets $(addprefix $(srcdir)/,$(WEBSOCKETS_JAVA_FILES))
 	$(JAR) cMf jars/websockets.jar -C classes/websockets .
 
 ifdef MOZ_WEBRTC
-jars/webrtc.jar: $(addprefix $(srcdir)/, $(WEBRTC_JAVA_FILES)) jars jars/gecko-browser.jar jars/gecko-util.jar
+jars/webrtc.jar: $(addprefix $(srcdir)/, $(WEBRTC_JAVA_FILES)) jars jars/gecko-browser.jar jars/gecko-util.jar jars/gecko-mozglue.jar
 	@echo "JAR webrtc.jar"
 	$(NSINSTALL) -D classes/webrtc
-	$(JAVAC) $(JAVAC_FLAGS) -d classes/webrtc -classpath classes/gecko-browser:classes/gecko-util $(addprefix $(srcdir)/,$(WEBRTC_JAVA_FILES))
+	$(JAVAC) $(JAVAC_FLAGS) -d classes/webrtc -classpath classes/gecko-browser:classes/gecko-util:classes/gecko-mozglue $(addprefix $(srcdir)/,$(WEBRTC_JAVA_FILES))
 	$(JAR) cMf jars/webrtc.jar -C classes/webrtc .
 endif
 
 jars:
 	@echo "MKDIR jars"
 	$(NSINSTALL) -D jars
 
 CLASSES_WITH_JNI= \
@@ -1290,16 +1294,31 @@ ifdef MOZ_WEBSMS_BACKEND
 # like the error message says and rebuild. All should be well after that.
 CLASSES_WITH_JNI += org.mozilla.gecko.GeckoSmsManager
 endif
 
 jni-stubs.inc: jars/gecko-browser.jar jars/gecko-mozglue.jar jars/gecko-util.jar jars/sync-thirdparty.jar
 	$(JAVAH) -o javah.out -bootclasspath $(JAVA_BOOTCLASSPATH) -classpath $(subst $(NULL) $(NULL),:,$^) $(CLASSES_WITH_JNI)
 	$(PYTHON) $(topsrcdir)/mobile/android/base/jni-generator.py javah.out $@
 
+ANNOTATION_PROCESSOR_JAVA_FILES = \
+  build/annotationProcessors/AnnotationProcessor.java \
+  build/annotationProcessors/CodeGenerator.java \
+  build/annotationProcessors/MethodWithAnnotationInfo.java \
+  build/annotationProcessors/classloader/IterableJarLoadingURLClassLoader.java \
+  build/annotationProcessors/classloader/JarClassIterator.java \
+  build/annotationProcessors/utils/AlphabeticMethodComparator.java \
+  build/annotationProcessors/utils/GeneratableEntryPointIterator.java \
+  build/annotationProcessors/utils/Utils.java \
+  $(NULL)
+
+GeneratedJNIWrappers.cpp: jars/gecko-browser.jar jars/gecko-mozglue.jar jars/gecko-util.jar jars/sync-thirdparty.jar
+	$(JAVAC) $(addprefix $(topsrcdir)/,$(ANNOTATION_PROCESSOR_JAVA_FILES)) -d $(CURDIR)
+	$(JAVA) -classpath $(JAVA_BOOTCLASSPATH):$(CURDIR) org.mozilla.gecko.annotationProcessors.AnnotationProcessor $^
+
 PP_RES_XML= \
   $(SYNC_PP_RES_XML) \
   $(FENNEC_PP_XML_FILES) \
   $(NULL)
 
 # This is kinda awful; if any of the source files change, we remake them all.
 $(PP_RES_XML): $(patsubst res/%,$(srcdir)/resources/%.in,$(PP_RES_XML))
 	$(PYTHON) $(topsrcdir)/config/Preprocessor.py \
@@ -1397,13 +1416,13 @@ R.java: $(all_resources)
 	$(AAPT) package -f -M AndroidManifest.xml -I $(ANDROID_SDK)/android.jar -S res -J . --custom-package org.mozilla.gecko --non-constant-id
 
 gecko.ap_: $(all_resources)
 	$(AAPT) package -f -M AndroidManifest.xml -I $(ANDROID_SDK)/android.jar  -S res -F $@
 
 fennec_ids.txt: fennec-ids-generator.py R.java
 	$(PYTHON) $(topsrcdir)/mobile/android/base/fennec-ids-generator.py -i R.java -o $@
 
-libs:: classes.dex package-name.txt jni-stubs.inc fennec_ids.txt
+libs:: classes.dex package-name.txt jni-stubs.inc GeneratedJNIWrappers.cpp fennec_ids.txt
 	$(INSTALL) classes.dex $(FINAL_TARGET)
 	$(INSTALL) package-name.txt $(FINAL_TARGET)
-	@(diff jni-stubs.inc $(topsrcdir)/mozglue/android/jni-stubs.inc >/dev/null) || \
-	 (echo "*** Error: The jni-stubs have changed. Copy $(CURDIR)/jni-stubs.inc to $(topsrcdir)/mozglue/android" && exit 1)
+	@(diff jni-stubs.inc $(topsrcdir)/mozglue/android/jni-stubs.inc >/dev/null && diff GeneratedJNIWrappers.cpp $(topsrcdir)/widget/android/GeneratedJNIWrappers.cpp >/dev/null) || \
+	 (echo "*** Error: The generated JNI code has changed. Please run cp $(CURDIR)/jni-stubs.inc $(topsrcdir)/mozglue/android && cp $(CURDIR)/GeneratedJNIWrappers.* $(topsrcdir)/widget/android and repeat the build." && exit 1)
--- a/mobile/android/base/ThumbnailHelper.java
+++ b/mobile/android/base/ThumbnailHelper.java
@@ -4,16 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko;
 
 import org.mozilla.gecko.db.BrowserDB;
 import org.mozilla.gecko.gfx.BitmapUtils;
 import org.mozilla.gecko.gfx.IntSize;
 import org.mozilla.gecko.mozglue.DirectBufferAllocator;
+import org.mozilla.gecko.mozglue.GeneratableAndroidBridgeTarget;
 
 import android.graphics.Bitmap;
 import android.util.Log;
 
 import java.nio.ByteBuffer;
 import java.util.LinkedList;
 import java.util.concurrent.atomic.AtomicInteger;
 
@@ -138,16 +139,17 @@ public final class ThumbnailHelper {
             return;
         }
 
         GeckoEvent e = GeckoEvent.createThumbnailEvent(tab.getId(), mWidth, mHeight, mBuffer);
         GeckoAppShell.sendEventToGecko(e);
     }
 
     /* This method is invoked by JNI once the thumbnail data is ready. */
+    @GeneratableAndroidBridgeTarget(stubName = "SendThumbnail")
     public static void notifyThumbnail(ByteBuffer data, int tabId, boolean success) {
         Tab tab = Tabs.getInstance().getTab(tabId);
         ThumbnailHelper helper = ThumbnailHelper.getInstance();
         if (success && tab != null) {
             helper.handleThumbnailData(tab, data);
         }
         helper.processNextThumbnail(tab);
     }
--- a/mobile/android/base/gfx/GLController.java
+++ b/mobile/android/base/gfx/GLController.java
@@ -3,16 +3,17 @@
  * 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/. */
 
 package org.mozilla.gecko.gfx;
 
 import org.mozilla.gecko.GeckoAppShell;
 import org.mozilla.gecko.GeckoEvent;
 import org.mozilla.gecko.GeckoThread;
+import org.mozilla.gecko.mozglue.GeneratableAndroidBridgeTarget;
 import org.mozilla.gecko.util.ThreadUtils;
 
 import android.util.Log;
 
 import javax.microedition.khronos.egl.EGL10;
 import javax.microedition.khronos.egl.EGLConfig;
 import javax.microedition.khronos.egl.EGLContext;
 import javax.microedition.khronos.egl.EGLDisplay;
@@ -257,17 +258,17 @@ public class GLController {
             if (red[0] == rSize && green[0] == gSize && blue[0] == bSize) {
                 return config;
             }
         }
 
         throw new GLControllerException("No suitable EGL configuration found");
     }
 
-    /* This function is invoked by JNI on the compositor thread */
+    @GeneratableAndroidBridgeTarget(allowMultithread = true, stubName = "ProvideEGLSurfaceWrapper")
     private EGLSurface provideEGLSurface() {
         return mEGLSurface;
     }
 
     private String getEGLError() {
         return "Error " + (mEGL == null ? "(no mEGL)" : mEGL.eglGetError());
     }
 
--- a/mobile/android/base/gfx/LayerView.java
+++ b/mobile/android/base/gfx/LayerView.java
@@ -7,16 +7,17 @@ package org.mozilla.gecko.gfx;
 
 import org.mozilla.gecko.GeckoAccessibility;
 import org.mozilla.gecko.GeckoAppShell;
 import org.mozilla.gecko.GeckoEvent;
 import org.mozilla.gecko.PrefsHelper;
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.TouchEventInterceptor;
 import org.mozilla.gecko.ZoomConstraints;
+import org.mozilla.gecko.mozglue.GeneratableAndroidBridgeTarget;
 import org.mozilla.gecko.util.EventDispatcher;
 
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.graphics.Color;
 import android.graphics.PixelFormat;
 import android.graphics.PointF;
@@ -502,17 +503,17 @@ public class LayerView extends FrameLayo
 
     public Object getNativeWindow() {
         if (mSurfaceView != null)
             return mSurfaceView.getHolder();
 
         return mTextureView.getSurfaceTexture();
     }
 
-    /** This function is invoked by Gecko (compositor thread) via JNI; be careful when modifying signature. */
+    @GeneratableAndroidBridgeTarget(allowMultithread = true, stubName = "RegisterCompositorWrapper")
     public static GLController registerCxxCompositor() {
         try {
             LayerView layerView = GeckoAppShell.getLayerView();
             GLController controller = layerView.getGLController();
             controller.compositorCreated();
             return controller;
         } catch (Exception e) {
             Log.e(LOGTAG, "Error registering compositor!", e);
--- a/mobile/android/base/gfx/NativePanZoomController.java
+++ b/mobile/android/base/gfx/NativePanZoomController.java
@@ -2,16 +2,17 @@
  * 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/. */
 
 package org.mozilla.gecko.gfx;
 
 import org.mozilla.gecko.GeckoEvent;
 import org.mozilla.gecko.GeckoThread;
+import org.mozilla.gecko.mozglue.GeneratableAndroidBridgeTarget;
 import org.mozilla.gecko.util.EventDispatcher;
 import org.mozilla.gecko.util.GeckoEventListener;
 
 import org.json.JSONObject;
 
 import android.graphics.PointF;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
@@ -77,22 +78,22 @@ class NativePanZoomController implements
     private native long runDelayedCallback();
 
     public native void destroy();
     public native void notifyDefaultActionPrevented(boolean prevented);
     public native boolean getRedrawHint();
     public native void setOverScrollMode(int overscrollMode);
     public native int getOverScrollMode();
 
-    /* Invoked from JNI */
+    @GeneratableAndroidBridgeTarget(allowMultithread = true, stubName = "RequestContentRepaintWrapper")
     private void requestContentRepaint(float x, float y, float width, float height, float resolution) {
         mTarget.forceRedraw(new DisplayPortMetrics(x, y, x + width, y + height, resolution));
     }
 
-    /* Invoked from JNI */
+    @GeneratableAndroidBridgeTarget(allowMultithread = true, stubName = "PostDelayedCallbackWrapper")
     private void postDelayedCallback(long delay) {
         mTarget.postDelayed(mCallbackRunnable, delay);
     }
 
     class CallbackRunnable implements Runnable {
         @Override
         public void run() {
             long nextDelay = runDelayedCallback();
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/mozglue/GeneratableAndroidBridgeTarget.java
@@ -0,0 +1,41 @@
+/* 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/. */
+
+package org.mozilla.gecko.mozglue;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * This annotation is used to tag methods that are to have wrapper methods generated in the android
+ * bridge. Such methods will be protected from destruction by Proguard, and allow us to avoid writing
+ * by hand large amounts of boring boilerplate.
+ * An annotated Java method will have a corresponding method generated in the android bridge.
+ * By default, the name of the generated method will be the same as the name of the Java method, with
+ * the first letter in upper case. The stubName property may be used to specify a custom name for the
+ * generated method stub.
+ *
+ * allowMultithreaded should be used as sparingly as possible - the resulting code will allow the
+ * Java method to be invoked from the C side from multiple threads. Often, this isn't what is wanted
+ * and may lead to subtle bugs.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+public @interface GeneratableAndroidBridgeTarget {
+    // Optional parameter specifying the name of the generated method stub. If omitted, the name
+    // of the Java method will be used.
+    String stubName() default "";
+
+    // Optional parameter specifying if the generated method should be a static member of AndroidBridge
+    // By default, an instance member is produced. This is almost always what is wanted.
+    boolean generateStatic() default false;
+
+    /**
+     * If set, the generated method stub will support being called from any thread via the use of
+     * GetJNIForThread. This is rarely useful, at time of writing, as well as possibly risky.
+     * See information in AndroidBridge.cpp regarding GetJNIForThread.
+     *
+     * Did I mention use of this function is discouraged?
+     */
+    boolean allowMultithread() default false;
+}
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/mozglue/OptionalGeneratedParameter.java
@@ -0,0 +1,18 @@
+/* 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/. */
+
+package org.mozilla.gecko.mozglue;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Used to annotate parameters which are optional on the C++ side of the bridge. The annotation is
+ * used by the annotation processor to generate the appropriate C++ headers so calls into the Java
+ * method all have the optional params set to the default value.
+ * The default values are zero for numerical types, false for booleans, "" for strings, and null
+ * for all other reference types.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+public @interface OptionalGeneratedParameter {}
--- a/mobile/android/base/util/Clipboard.java
+++ b/mobile/android/base/util/Clipboard.java
@@ -4,16 +4,18 @@
 
 package org.mozilla.gecko.util;
 
 import android.content.ClipData;
 import android.content.Context;
 import android.os.Build;
 import android.util.Log;
 
+import org.mozilla.gecko.mozglue.GeneratableAndroidBridgeTarget;
+
 import java.util.concurrent.SynchronousQueue;
 
 public final class Clipboard {
     private static Context mContext;
     private final static String LOG_TAG = "Clipboard";
     private final static SynchronousQueue<String> sClipboardQueue = new SynchronousQueue<String>();
 
     private Clipboard() {
@@ -22,16 +24,17 @@ public final class Clipboard {
     public static void init(Context c) {
         if (mContext != null) {
             Log.w(LOG_TAG, "Clipboard.init() called twice!");
             return;
         }
         mContext = c;
     }
 
+    @GeneratableAndroidBridgeTarget(stubName = "GetClipboardTextWrapper")
     public static String getText() {
         // If we're on the UI thread or the background thread, we have a looper on the thread
         // and can just call this directly. For any other threads, post the call to the
         // background thread.
 
         if (ThreadUtils.isOnUiThread() || ThreadUtils.isOnBackgroundThread()) {
             return getClipboardTextImpl();
         }
@@ -47,16 +50,17 @@ public final class Clipboard {
         });
         try {
             return sClipboardQueue.take();
         } catch (InterruptedException ie) {
             return "";
         }
     }
 
+    @GeneratableAndroidBridgeTarget(stubName = "SetClipboardText")
     public static void setText(final CharSequence text) {
         ThreadUtils.postToBackgroundThread(new Runnable() {
             @Override
             @SuppressWarnings("deprecation")
             public void run() {
                 if (Build.VERSION.SDK_INT >= 11) {
                     android.content.ClipboardManager cm = getClipboardManager11(mContext);
                     ClipData clip = ClipData.newPlainText("Text", text);
--- a/testing/mochitest/browser-test.js
+++ b/testing/mochitest/browser-test.js
@@ -83,16 +83,17 @@ Tester.prototype = {
   Promise: null,
 
   repeat: 0,
   runUntilFailure: false,
   checker: null,
   currentTestIndex: -1,
   lastStartTime: null,
   openedWindows: null,
+  lastAssertionCount: 0,
 
   get currentTest() {
     return this.tests[this.currentTestIndex];
   },
   get done() {
     return this.currentTestIndex == this.tests.length - 1;
   },
 
@@ -310,16 +311,49 @@ Tester.prototype = {
       // Notify a long running test problem if it didn't end up in a timeout.
       if (this.currentTest.unexpectedTimeouts && !this.currentTest.timedOut) {
         let msg = "This test exceeded the timeout threshold. It should be " +
                   "rewritten or split up. If that's not possible, use " +
                   "requestLongerTimeout(N), but only as a last resort.";
         this.currentTest.addResult(new testResult(false, msg, "", false));
       }
 
+      // If we're in a debug build, check assertion counts.  This code
+      // is similar to the code in TestRunner.testUnloaded in
+      // TestRunner.js used for all other types of mochitests.
+      let debugsvc = Cc["@mozilla.org/xpcom/debug;1"].getService(Ci.nsIDebug2);
+      if (debugsvc.isDebugBuild) {
+        let newAssertionCount = debugsvc.assertionCount;
+        let numAsserts = newAssertionCount - this.lastAssertionCount;
+        this.lastAssertionCount = newAssertionCount;
+
+        let max = testScope.__expectedMaxAsserts;
+        let min = testScope.__expectedMinAsserts;
+        if (numAsserts > max) {
+          let msg = "Assertion count " + numAsserts +
+                    " is greater than expected range " +
+                    min + "-" + max + " assertions.";
+          // TEST-UNEXPECTED-FAIL (TEMPORARILY TEST-KNOWN-FAIL)
+          //this.currentTest.addResult(new testResult(false, msg, "", false));
+          this.currentTest.addResult(new testResult(true, msg, "", true));
+        } else if (numAsserts < min) {
+          let msg = "Assertion count " + numAsserts +
+                    " is less than expected range " +
+                    min + "-" + max + " assertions.";
+          // TEST-UNEXPECTED-PASS
+          this.currentTest.addResult(new testResult(false, msg, "", true));
+        } else if (numAsserts > 0) {
+          let msg = "Assertion count " + numAsserts +
+                    " is within expected range " +
+                    min + "-" + max + " assertions.";
+          // TEST-KNOWN-FAIL
+          this.currentTest.addResult(new testResult(true, msg, "", true));
+        }
+      }
+
       // Note the test run time
       let time = Date.now() - this.lastStartTime;
       this.dumper.dump("INFO TEST-END | " + this.currentTest.path + " | finished in " + time + "ms\n");
       this.currentTest.setDuration(time);
 
       testScope.destroy();
       this.currentTest.scope = null;
     }
@@ -401,17 +435,17 @@ Tester.prototype = {
     // Import utils in the test scope.
     this.currentTest.scope.EventUtils = this.EventUtils;
     this.currentTest.scope.SimpleTest = this.SimpleTest;
     this.currentTest.scope.gTestPath = this.currentTest.path;
     this.currentTest.scope.Task = this.Task;
     this.currentTest.scope.Promise = this.Promise;
 
     // Override SimpleTest methods with ours.
-    ["ok", "is", "isnot", "ise", "todo", "todo_is", "todo_isnot", "info"].forEach(function(m) {
+    ["ok", "is", "isnot", "ise", "todo", "todo_is", "todo_isnot", "info", "expectAssertions"].forEach(function(m) {
       this.SimpleTest[m] = this[m];
     }, this.currentTest.scope);
 
     //load the tools to work with chrome .jar and remote
     try {
       this._scriptLoader.loadSubScript("chrome://mochikit/content/chrome-harness.js", this.currentTest.scope);
     } catch (ex) { /* no chrome-harness tools */ }
 
@@ -678,16 +712,30 @@ function testScope(aTester, aTest) {
   this.expectUncaughtException = function test_expectUncaughtException(aExpecting) {
     self.SimpleTest.expectUncaughtException(aExpecting);
   };
 
   this.ignoreAllUncaughtExceptions = function test_ignoreAllUncaughtExceptions(aIgnoring) {
     self.SimpleTest.ignoreAllUncaughtExceptions(aIgnoring);
   };
 
+  this.expectAssertions = function test_expectAssertions(aMin, aMax) {
+    let min = aMin;
+    let max = aMax;
+    if (typeof(max) == "undefined") {
+      max = min;
+    }
+    if (typeof(min) != "number" || typeof(max) != "number" ||
+        min < 0 || max < min) {
+      throw "bad parameter to expectAssertions";
+    }
+    self.__expectedMinAsserts = min;
+    self.__expectedMaxAsserts = max;
+  };
+
   this.finish = function test_finish() {
     self.__done = true;
     if (self.__waitTimer) {
       self.executeSoon(function() {
         if (self.__done && self.__waitTimer) {
           clearTimeout(self.__waitTimer);
           self.__waitTimer = null;
           self.__tester.nextTest();
@@ -698,16 +746,18 @@ function testScope(aTester, aTest) {
 }
 testScope.prototype = {
   __done: true,
   __generator: null,
   __tasks: null,
   __waitTimer: null,
   __cleanupFunctions: [],
   __timeoutFactor: 1,
+  __expectedMinAsserts: 0,
+  __expectedMaxAsserts: 0,
 
   EventUtils: {},
   SimpleTest: {},
   Task: null,
   Promise: null,
 
   /**
    * Add a test function which is a Task function.
--- a/testing/mochitest/tests/SimpleTest/TestRunner.js
+++ b/testing/mochitest/tests/SimpleTest/TestRunner.js
@@ -457,16 +457,19 @@ TestRunner.testFinished = function(tests
 
     SpecialPowers.executeAfterFlushingMessageQueue(function() {
         cleanUpCrashDumpFiles();
         SpecialPowers.flushPermissions(function () { SpecialPowers.flushPrefEnv(runNextTest); });
     });
 };
 
 TestRunner.testUnloaded = function() {
+    // If we're in a debug build, check assertion counts.  This code is
+    // similar to the code in Tester_nextTest in browser-test.js used
+    // for browser-chrome mochitests.
     if (SpecialPowers.isDebugBuild) {
         var newAssertionCount = SpecialPowers.assertionCount();
         var numAsserts = newAssertionCount - TestRunner._lastAssertionCount;
         TestRunner._lastAssertionCount = newAssertionCount;
 
         var url = TestRunner._urls[TestRunner._currentTest];
         var max = TestRunner._expectedMaxAsserts;
         var min = TestRunner._expectedMinAsserts;
--- a/toolkit/components/downloads/nsDownloadManager.cpp
+++ b/toolkit/components/downloads/nsDownloadManager.cpp
@@ -2770,17 +2770,17 @@ nsDownload::SetState(DownloadState aStat
 #ifdef MOZ_WIDGET_ANDROID
         nsCOMPtr<nsIMIMEInfo> mimeInfo;
         nsAutoCString contentType;
         GetMIMEInfo(getter_AddRefs(mimeInfo));
 
         if (mimeInfo)
           mimeInfo->GetMIMEType(contentType);
 
-        mozilla::AndroidBridge::Bridge()->ScanMedia(path, contentType);
+        mozilla::AndroidBridge::Bridge()->ScanMedia(path, NS_ConvertUTF8toUTF16(contentType));
 #endif
       }
 
 #ifdef XP_WIN
       // Adjust file attributes so that by default, new files are indexed
       // by desktop search services. Skip off those that land in the temp
       // folder.
       nsCOMPtr<nsIFile> tempDir, fileDir;
--- a/toolkit/components/jsdownloads/src/DownloadPlatform.cpp
+++ b/toolkit/components/jsdownloads/src/DownloadPlatform.cpp
@@ -113,17 +113,17 @@ nsresult DownloadPlatform::DownloadDone(
                                              kCFStringEncodingUTF8);
     CFNotificationCenterRef center = ::CFNotificationCenterGetDistributedCenter();
     ::CFNotificationCenterPostNotification(center, CFSTR("com.apple.DownloadFileFinished"),
                                            observedObject, NULL, TRUE);
     ::CFRelease(observedObject);
 #endif
 #ifdef MOZ_WIDGET_ANDROID
     if (!aContentType.IsEmpty()) {
-      mozilla::AndroidBridge::Bridge()->ScanMedia(path, aContentType);
+      mozilla::AndroidBridge::Bridge()->ScanMedia(path, NS_ConvertUTF8toUTF16(aContentType));
     }
 #endif
   }
 
 #ifdef XP_WIN
   // Adjust file attributes so that by default, new files are indexed by
   // desktop search services. Skip off those that land in the temp folder.
   nsCOMPtr<nsIFile> tempDir, fileDir;
--- a/toolkit/xre/nsAndroidStartup.cpp
+++ b/toolkit/xre/nsAndroidStartup.cpp
@@ -26,20 +26,20 @@
 // with local references overrunning the local refs table, among
 // other things, since GC can't ever run on them.
 
 // Note that we don't have xpcom initialized yet, so we can't use the
 // thread manager for this.  Instead, we use pthreads directly.
 
 struct AutoAttachJavaThread {
     AutoAttachJavaThread() {
-        attached = mozilla_AndroidBridge_SetMainThread((void*)pthread_self());
+        attached = mozilla_AndroidBridge_SetMainThread(pthread_self());
     }
     ~AutoAttachJavaThread() {
-        mozilla_AndroidBridge_SetMainThread(nullptr);
+        mozilla_AndroidBridge_SetMainThread(-1);
         attached = false;
     }
 
     bool attached;
 };
 
 extern "C" NS_EXPORT void
 GeckoStart(void *data, const nsXREAppData *appData)
--- a/uriloader/exthandler/android/nsAndroidHandlerApp.cpp
+++ b/uriloader/exthandler/android/nsAndroidHandlerApp.cpp
@@ -66,23 +66,23 @@ NS_IMETHODIMP
 nsAndroidHandlerApp::LaunchWithURI(nsIURI *aURI, nsIInterfaceRequestor *aWindowContext)
 {
   if (!mozilla::AndroidBridge::Bridge())
     return NS_ERROR_FAILURE;
 
   nsCString uriSpec;
   aURI->GetSpec(uriSpec);
   return mozilla::AndroidBridge::Bridge()->
-    OpenUriExternal(uriSpec, mMimeType, mPackageName, mClassName, mAction) ? 
+    OpenUriExternal(NS_ConvertUTF8toUTF16(uriSpec), NS_ConvertUTF8toUTF16(mMimeType), mPackageName, mClassName, mAction) ? 
     NS_OK : NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
 nsAndroidHandlerApp::Share(const nsAString & data, const nsAString & title)
 {
   if (!mozilla::AndroidBridge::Bridge())
     return NS_ERROR_FAILURE;
 
   return mozilla::AndroidBridge::Bridge()->
-    OpenUriExternal(NS_ConvertUTF16toUTF8(data), mMimeType, mPackageName, 
+    OpenUriExternal(data, NS_ConvertUTF8toUTF16(mMimeType), mPackageName, 
                     mClassName, mAction) ? NS_OK : NS_ERROR_FAILURE;
 }
 
--- a/uriloader/exthandler/android/nsExternalSharingAppService.cpp
+++ b/uriloader/exthandler/android/nsExternalSharingAppService.cpp