Merge mozilla-central to mozilla-inbound
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Thu, 03 Jul 2014 14:16:06 +0200
changeset 192187 1022c98a62d29d3ce9ee3dc1cb0276b79ad09bf3
parent 192186 94f150f5b21fb3e1a4266a0f333751f0c932ab9b (current diff)
parent 192089 ac6960197eb6bdd5501a7ddd23dcda6976a8a9e0 (diff)
child 192188 a481b4bc3ae666ed2e168291cb11fd04d0899064
push idunknown
push userunknown
push dateunknown
milestone33.0a1
Merge mozilla-central to mozilla-inbound
--- a/addon-sdk/source/lib/sdk/addon/runner.js
+++ b/addon-sdk/source/lib/sdk/addon/runner.js
@@ -120,18 +120,19 @@ function startup(reason, options) {
 function run(options) {
   try {
     // Try initializing HTML localization before running main module. Just print
     // an exception in case of error, instead of preventing addon to be run.
     try {
       // Do not enable HTML localization while running test as it is hard to
       // disable. Because unit tests are evaluated in a another Loader who
       // doesn't have access to this current loader.
-      if (options.main !== 'test-harness/run-tests')
+      if (options.main !== 'sdk/test/runner') {
         require('../l10n/html').enable();
+      }
     }
     catch(error) {
       console.exception(error);
     }
 
     // native-options does stuff directly with preferences key from package.json
     if (preferences && preferences.length > 0) {
       try {
--- a/addon-sdk/source/lib/sdk/context-menu.js
+++ b/addon-sdk/source/lib/sdk/context-menu.js
@@ -254,19 +254,23 @@ function populateCallbackNodeData(node) 
   }
   else {
     data.isEditable = node.isContentEditable;
   }
 
   data.selectionText = selection.text;
   
   data.srcURL = node.src || null;
-  data.linkURL = node.href || null;
   data.value = node.value || null;
 
+  while (!data.linkURL && node) {
+    data.linkURL = node.href || null;
+    node = node.parentNode;
+  }
+  
   return data;
 }
 
 function removeItemFromArray(array, item) {
   return array.filter(function(i) i !== item);
 }
 
 // Converts anything that isn't false, null or undefined into a string
--- a/addon-sdk/source/lib/sdk/deprecated/unit-test-finder.js
+++ b/addon-sdk/source/lib/sdk/deprecated/unit-test-finder.js
@@ -5,29 +5,33 @@
 
 module.metadata = {
   "stability": "deprecated"
 };
 
 const file = require("../io/file");
 const memory = require('./memory');
 const { Loader } = require("../test/loader");
-const cuddlefish = require("../loader/cuddlefish");
+
+const { isNative } = require('@loader/options');
+
+const cuddlefish = isNative ? require("toolkit/loader") : require("../loader/cuddlefish");
+
 const { defer, resolve } = require("../core/promise");
 const { getAddon } = require("../addon/installer");
 const { id } = require("sdk/self");
 const { newURI } = require('sdk/url/utils');
 const { getZipReader } = require("../zip/utils");
 
 const { Cc, Ci, Cu } = require("chrome");
 const { AddonManager } = Cu.import("resource://gre/modules/AddonManager.jsm", {});
 var ios = Cc['@mozilla.org/network/io-service;1']
           .getService(Ci.nsIIOService);
 
-const TEST_REGEX = /(([^\/]+\/)(?:lib\/)?)(tests?\/test-[^\.\/]+)\.js$/;
+const TEST_REGEX = /(([^\/]+\/)(?:lib\/)?)?(tests?\/test-[^\.\/]+)\.js$/;
 
 const { mapcat, map, filter, fromEnumerator } = require("sdk/util/sequence");
 
 const toFile = x => x.QueryInterface(Ci.nsIFile);
 const isTestFile = ({leafName}) => leafName.substr(0, 5) == "test-" && leafName.substr(-3, 3) == ".js";
 const getFileURI = x => ios.newFileURI(x).spec;
 
 const getDirectoryEntries = file => map(toFile, fromEnumerator(_ => file.directoryEntries));
@@ -49,18 +53,19 @@ const removeDups = (array) => array.redu
 const getSuites = function getSuites({ id }) {
   return getAddon(id).then(addon => {
     let fileURI = addon.getResourceURI("tests/");
     let isPacked = fileURI.scheme == "jar";
     let xpiURI = addon.getResourceURI();
     let file = xpiURI.QueryInterface(Ci.nsIFileURL).file;
     let suites = [];
     let addEntry = (entry) => {
-      if (TEST_REGEX.test(entry)) {
-        let suite = RegExp.$2 + RegExp.$3;
+      let pass = TEST_REGEX.test(entry);
+      if (pass) {
+        let suite = (isNative ? "./" : "") + RegExp.$2 + RegExp.$3;
         suites.push(suite);
       }
     }
 
     if (isPacked) {
       return getZipReader(file).then(zip => {
         let entries = zip.findEntries(null);
         while (entries.hasMore()) {
--- a/addon-sdk/source/lib/sdk/deprecated/unit-test.js
+++ b/addon-sdk/source/lib/sdk/deprecated/unit-test.js
@@ -7,17 +7,17 @@ module.metadata = {
   "stability": "deprecated"
 };
 
 const memory = require('./memory');
 const timer = require("../timers");
 var cfxArgs = require("@test/options");
 const { getTabs, getURI } = require("../tabs/utils");
 const { windows, isBrowser } = require("../window/utils");
-const { defer } = require("../core/promise");
+const { defer, all } = require("../core/promise");
 
 const findAndRunTests = function findAndRunTests(options) {
   var TestFinder = require("./unit-test-finder").TestFinder;
   var finder = new TestFinder({
     filter: options.filter,
     testInProcess: options.testInProcess,
     testOutOfProcess: options.testOutOfProcess
   });
@@ -280,53 +280,68 @@ TestRunner.prototype = {
         else {
           this.console.error("fail:", "Empty test")
         }
         this.failed++;
         this.test.failed++;
       }
 
       let wins = windows(null, { includePrivate: true });
-      let tabs = [];
-      for (let win of wins.filter(isBrowser)) {
-        for (let tab of getTabs(win)) {
-          tabs.push(tab);
+      let winPromises = wins.map(win =>  {
+        let { promise, resolve } = defer();
+        if (["interactive", "complete"].indexOf(win.document.readyState) >= 0) {
+          resolve()
         }
-      }
+        else {
+          win.addEventListener("DOMContentLoaded", function onLoad() {
+            win.removeEventListener("DOMContentLoaded", onLoad, false);
+            resolve();
+          }, false);
+        }
+        return promise;
+      });
 
-      if (wins.length != 1)
-        this.fail("Should not be any unexpected windows open");
-      if (tabs.length != 1)
-        this.fail("Should not be any unexpected tabs open");
-      if (tabs.length != 1 || wins.length != 1) {
-        console.log("Windows open:");
-        for (let win of wins) {
-          if (isBrowser(win)) {
-            tabs = getTabs(win);
-            console.log(win.location + " - " + tabs.map(getURI).join(", "));
-          }
-          else {
-            console.log(win.location);
+      all(winPromises).then(_ => {
+        let tabs = [];
+        for (let win of wins.filter(isBrowser)) {
+          for (let tab of getTabs(win)) {
+            tabs.push(tab);
           }
         }
-      }
 
-      this.testRunSummary.push({
-        name: this.test.name,
-        passed: this.test.passed,
-        failed: this.test.failed,
-        errors: [error for (error in this.test.errors)].join(", ")
+        if (wins.length != 1)
+          this.fail("Should not be any unexpected windows open");
+        if (tabs.length != 1)
+          this.fail("Should not be any unexpected tabs open");
+        if (tabs.length != 1 || wins.length != 1) {
+          console.log("Windows open:");
+          for (let win of wins) {
+            if (isBrowser(win)) {
+              tabs = getTabs(win);
+              console.log(win.location + " - " + tabs.map(getURI).join(", "));
+            }
+            else {
+              console.log(win.location);
+            }
+          }
+        }
+
+        this.testRunSummary.push({
+          name: this.test.name,
+          passed: this.test.passed,
+          failed: this.test.failed,
+          errors: [error for (error in this.test.errors)].join(", ")
+        });
+
+        if (this.onDone !== null) {
+          let onDone = this.onDone;
+          this.onDone = null;
+          timer.setTimeout(_ => onDone(this), 0);
+        }
       });
-
-      if (this.onDone !== null) {
-        var onDone = this.onDone;
-        var self = this;
-        this.onDone = null;
-        timer.setTimeout(function() { onDone(self); }, 0);
-      }
     }
   },
 
   // Set of assertion functions to wait for an assertion to become true
   // These functions take the same arguments as the TestRunner.assert* methods.
   waitUntil: function waitUntil() {
     return this._waitUntil(this.assert, arguments);
   },
--- a/addon-sdk/source/lib/sdk/tabs/tab-fennec.js
+++ b/addon-sdk/source/lib/sdk/tabs/tab-fennec.js
@@ -2,19 +2,19 @@
  * 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/. */
 'use strict';
 
 const { Cc, Ci } = require('chrome');
 const { Class } = require('../core/heritage');
 const { tabNS, rawTabNS } = require('./namespace');
 const { EventTarget } = require('../event/target');
-const { activateTab, getTabTitle, setTabTitle, closeTab, getTabURL, getTabContentWindow,
-        getTabForBrowser,
-        setTabURL, getOwnerWindow, getTabContentType, getTabId } = require('./utils');
+const { activateTab, getTabTitle, setTabTitle, closeTab, getTabURL,
+        getTabContentWindow, getTabForBrowser, setTabURL, getOwnerWindow,
+        getTabContentDocument, getTabContentType, getTabId } = require('./utils');
 const { emit } = require('../event/core');
 const { isPrivate } = require('../private-browsing/utils');
 const { isWindowPrivate } = require('../window/utils');
 const { when: unload } = require('../system/unload');
 const { viewFor } = require('../view/core');
 const { EVENTS } = require('./events');
 
 const ERR_FENNEC_MSG = 'This method is not yet supported by Fennec';
@@ -92,16 +92,24 @@ const Tab = Class({
   getThumbnail: function() {
     // TODO: implement!
     console.error(ERR_FENNEC_MSG);
 
     // return 80x45 blank default
     return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAAAtCAYAAAA5reyyAAAAJElEQVRoge3BAQEAAACCIP+vbkhAAQAAAAAAAAAAAAAAAADXBjhtAAGQ0AF/AAAAAElFTkSuQmCC';
   },
 
+  /**
+   * tab's document readyState, or 'uninitialized' if it doesn't even exist yet.
+   */
+  get readyState() {
+    let doc = getTabContentDocument(tabNS(this).tab);
+    return doc && doc.readyState || 'uninitialized';
+  },
+
   get id() {
     return getTabId(tabNS(this).tab);
   },
 
   /**
    * The index of the tab relative to other tabs in the application window.
    * Changing this property will change order of the actual position of the tab.
    * @type {Number}
--- a/addon-sdk/source/lib/sdk/tabs/tab-firefox.js
+++ b/addon-sdk/source/lib/sdk/tabs/tab-firefox.js
@@ -5,18 +5,19 @@
 
 const { Trait } = require("../deprecated/traits");
 const { EventEmitter } = require("../deprecated/events");
 const { defer } = require("../lang/functional");
 const { has } = require("../util/array");
 const { EVENTS } = require("./events");
 const { getThumbnailURIForWindow } = require("../content/thumbnail");
 const { getFaviconURIForLocation } = require("../io/data");
-const { activateTab, getOwnerWindow, getBrowserForTab, getTabTitle, setTabTitle,
-        getTabURL, setTabURL, getTabContentType, getTabId } = require('./utils');
+const { activateTab, getOwnerWindow, getBrowserForTab, getTabTitle,
+        setTabTitle, getTabContentDocument, getTabURL, setTabURL,
+        getTabContentType, getTabId } = require('./utils');
 const { isPrivate } = require('../private-browsing/utils');
 const { isWindowPrivate } = require('../window/utils');
 const viewNS = require('../core/namespace').ns();
 const { deprecateUsage } = require('../util/deprecate');
 const { getURL } = require('../url/utils');
 const { viewFor } = require('../view/core');
 const { observer } = require('./observer');
 
@@ -138,23 +139,31 @@ const TabTrait = Trait.compose(EventEmit
   get _browser() getBrowserForTab(this._tab),
   /**
    * Window DOM element containing this tab.
    */
   get _window() getOwnerWindow(this._tab),
   /**
    * Document object of the page that is currently loaded in this tab.
    */
-  get _contentDocument() this._browser.contentDocument,
+  get _contentDocument() getTabContentDocument(this._tab),
   /**
    * Window object of the page that is currently loaded in this tab.
    */
   get _contentWindow() this._browser.contentWindow,
 
   /**
+   * tab's document readyState, or 'uninitialized' if it doesn't even exist yet.
+   */
+  get readyState() {
+    let doc = this._contentDocument;
+    return doc && doc.readyState || 'uninitialized';
+  },
+
+  /**
    * Unique id for the tab, actually maps to tab.linkedPanel but with some munging.
    */
   get id() this._tab ? getTabId(this._tab) : undefined,
 
   /**
    * The title of the page currently loaded in the tab.
    * Changing this property changes an actual title.
    * @type {String}
--- a/addon-sdk/source/lib/sdk/tabs/utils.js
+++ b/addon-sdk/source/lib/sdk/tabs/utils.js
@@ -221,16 +221,21 @@ exports.getTabTitle = getTabTitle;
 function setTabTitle(tab, title) {
   title = String(title);
   if (tab.browser)
     tab.browser.contentDocument.title = title;
   tab.label = String(title);
 }
 exports.setTabTitle = setTabTitle;
 
+function getTabContentDocument(tab) {
+  return getBrowserForTab(tab).contentDocument;
+}
+exports.getTabContentDocument = getTabContentDocument;
+
 function getTabContentWindow(tab) {
   return getBrowserForTab(tab).contentWindow;
 }
 exports.getTabContentWindow = getTabContentWindow;
 
 /**
  * Returns all tabs' content windows across all the browsers' windows
  */
--- a/addon-sdk/source/lib/sdk/test.js
+++ b/addon-sdk/source/lib/sdk/test.js
@@ -1,36 +1,26 @@
 /* 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/. */
-
 "use strict";
 
 module.metadata = {
   "stability": "unstable"
 };
 
 const { Cu } = require("chrome");
 const { Task } = Cu.import("resource://gre/modules/Task.jsm", {});
 const { defer } = require("sdk/core/promise");
 const BaseAssert = require("sdk/test/assert").Assert;
 const { isFunction, isObject } = require("sdk/lang/type");
+const { extend } = require("sdk/util/object");
 
 exports.Assert = BaseAssert;
 
-function extend(target) {
-  let descriptor = {}
-  Array.slice(arguments, 1).forEach(function(source) {
-    Object.getOwnPropertyNames(source).forEach(function onEach(name) {
-      descriptor[name] = Object.getOwnPropertyDescriptor(source, name);
-    });
-  });
-  return Object.create(target, descriptor);
-}
-
 /**
  * Function takes test `suite` object in CommonJS format and defines all of the
  * tests from that suite and nested suites in a jetpack format on a given
  * `target` object. Optionally third argument `prefix` can be passed to prefix
  * all the test names.
  */
 function defineTestSuite(target, suite, prefix) {
   prefix = prefix || "";
@@ -105,17 +95,16 @@ function defineTestSuite(target, suite, 
 
 /**
  * This function is a CommonJS test runner function, but since Jetpack test
  * runner and test format is different from CommonJS this function shims given
  * `exports` with all its tests into a Jetpack test format so that the built-in
  * test runner will be able to run CommonJS test without manual changes.
  */
 exports.run = function run(exports) {
-
   // We can't leave old properties on exports since those are test in a CommonJS
   // format that why we move everything to a new `suite` object.
   let suite = {};
   Object.keys(exports).forEach(function(key) {
     suite[key] = exports[key];
     delete exports[key];
   });
 
--- a/addon-sdk/source/python-lib/cuddlefish/__init__.py
+++ b/addon-sdk/source/python-lib/cuddlefish/__init__.py
@@ -786,17 +786,16 @@ def run(arguments=sys.argv[1:], target_c
     if os.getcwd() == env_root:
         options.bundle_sdk = True
         options.force_use_bundled_sdk = False
         options.overload_modules = True
 
     extra_environment = {}
     if command == "test":
         # This should be contained in the test runner package.
-        # maybe just do: target_cfg.main = 'test-harness/run-tests'
         harness_options['main'] = 'sdk/test/runner'
         harness_options['mainPath'] = 'sdk/test/runner'
     else:
         harness_options['main'] = target_cfg.get('main')
         harness_options['mainPath'] = manifest.top_path
     extra_environment["CFX_COMMAND"] = command
 
     for option in inherited_options:
--- a/addon-sdk/source/test/test-context-menu.html
+++ b/addon-sdk/source/test/test-context-menu.html
@@ -58,16 +58,33 @@
 
     <p>
       <a class="predicate-test-a" href="#test">
         A link with no ID and an anchor, used by PredicateContext tests.
       </a>
     </p>
 
     <p>
+      <a class="predicate-test-b" href="#nested-image">
+        <img id="predicate-test-nested-image" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAHWSURBVHjaYvz//z8DJQAggJiQOe/fv2fv7Oz8rays/N+VkfG/iYnJfyD/1+rVq7ffu3dPFpsBAAHEAHIBCJ85c8bN2Nj4vwsDw/8zQLwKiO8CcRoQu0DxqlWrdsHUwzBAAIGJmTNnPgYa9j8UqhFElwPxf2MIDeIrKSn9FwSJoRkAEEAM0DD4DzMAyPi/G+QKY4hh5WAXGf8PDQ0FGwJ22d27CjADAAIIrLmjo+MXA9R2kAHvGBA2wwx6B8W7od6CeQcggKCmCEL8bgwxYCbUIGTDVkHDBia+CuotgACCueD3TDQN75D4xmAvCoK9ARMHBzAw0AECiBHkAlC0Mdy7x9ABNA3obAZXIAa6iKEcGlMVQHwWyjYuL2d4v2cPg8vZswx7gHyAAAK7AOif7SAbOqCmn4Ha3AHFsIDtgPq/vLz8P4MSkJ2W9h8ggBjevXvHDo4FQUQg/kdypqCg4H8lUIACnQ/SOBMYI8bAsAJFPcj1AAEEjwVQqLpAbXmH5BJjqI0gi9DTAAgDBBCcAVLkgmQ7yKCZxpCQxqUZhAECCJ4XgMl493ug21ZD+aDAXH0WLM4A9MZPXJkJIIAwTAR5pQMalaCABQUULttBGCCAGCnNzgABBgAMJ5THwGvJLAAAAABJRU5ErkJggg==">
+      </a>
+    </p>
+    <p>
+      <a class="predicate-test-c" href="#nested-structure">
+        <span>
+          <span>
+            <span id="predicate-test-nested-structure">
+              A complex nested structure.
+            </span>
+          </span>
+        </span>
+      </a>
+    </p>
+
+    <p>
       <input type="text" id="textbox" value="test value">
     </p>
 
     <p>
       <input type="text" id="readonly-textbox" readonly="true" value="readonly value">
     </p>
 
     <p>
--- a/addon-sdk/source/test/test-context-menu.js
+++ b/addon-sdk/source/test/test-context-menu.js
@@ -3567,16 +3567,58 @@ exports.testPredicateContextTargetLinkNo
   test.withTestDoc(function (window, doc) {
     test.showMenu(doc.getElementById("image"), function (popup) {
       test.checkMenu(items, [], []);
       test.done();
     });
   });
 };
 
+// Test that the data object has the correct link for a nested image
+exports.testPredicateContextTargetLinkSetNestedImage = function (assert, done) {
+  let test = new TestHelper(assert, done);
+  let loader = test.newLoader();
+
+  let items = [loader.cm.Item({
+    label: "item",
+    context: loader.cm.PredicateContext(function (data) {
+      assert.strictEqual(data.linkURL, TEST_DOC_URL + "#nested-image");
+      return true;
+    })
+  })];
+
+  test.withTestDoc(function (window, doc) {
+    test.showMenu(doc.getElementById("predicate-test-nested-image"), function (popup) {
+      test.checkMenu(items, [], []);
+      test.done();
+    });
+  });
+};
+
+// Test that the data object has the correct link for a complex nested structure
+exports.testPredicateContextTargetLinkSetNestedStructure = function (assert, done) {
+  let test = new TestHelper(assert, done);
+  let loader = test.newLoader();
+
+  let items = [loader.cm.Item({
+    label: "item",
+    context: loader.cm.PredicateContext(function (data) {
+      assert.strictEqual(data.linkURL, TEST_DOC_URL + "#nested-structure");
+      return true;
+    })
+  })];
+
+  test.withTestDoc(function (window, doc) {
+    test.showMenu(doc.getElementById("predicate-test-nested-structure"), function (popup) {
+      test.checkMenu(items, [], []);
+      test.done();
+    });
+  });
+};
+
 // Test that the data object has the value for an input textbox
 exports.testPredicateContextTargetValueSet = function (assert, done) {
   let test = new TestHelper(assert, done);
   let loader = test.newLoader();
   let image;
 
   let items = [loader.cm.Item({
     label: "item",
--- a/addon-sdk/source/test/test-tab.js
+++ b/addon-sdk/source/test/test-tab.js
@@ -167,9 +167,27 @@ exports["test modelFor(xulTab)"] = (asse
       assert.equal(getTabId(view), tab.id, "tab has a same id");
       assert.equal(modelFor(view), tab, "modelFor(view) is SDK tab");
 
       tab.close(defer(done));
     }
   });
 };
 
+exports["test tab.readyState"] = (assert, done) => {
+  tabs.open({
+    url: "data:text/html;charset=utf-8,test_readyState",
+    onOpen: (tab) => {
+      assert.equal(tab.readyState, "uninitialized",
+        "tab is 'uninitialized' when opened");
+    },
+    onReady: (tab) => {
+      assert.notEqual(["interactive", "complete"].indexOf(tab.readyState), -1,
+        "tab is either interactive or complete when onReady");
+    },
+    onLoad: (tab) => {
+      assert.equal(tab.readyState, "complete", "tab is complete onLoad");
+      tab.close(defer(done));
+    }
+  });
+}
+
 require("sdk/test").run(exports);
--- a/b2g/config/emulator-ics/sources.xml
+++ b/b2g/config/emulator-ics/sources.xml
@@ -14,17 +14,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="2a165bebfa19b11b697837409f9550dd2917c46c">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="6848631d1fe8baf973e3d1257f7b50427295477b"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="d7a517f0bde32072f1799e4a47ea34c6d786c287"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="230f11aff069d90d20fc2dc63b48e9ae3d4bdcd1"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="3326b51017252e09ccdd715dec6c5e12a7d1ecfe"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="96cdde4b5b5d8d3785b36c3c68cd746aff3005cc"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="7f9ec13a30f1b2cc8bdb1a199b7da54b9ab8860f"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/emulator-jb/sources.xml
+++ b/b2g/config/emulator-jb/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="cc67f31dc638c0b7edba3cf7e3d87cadf0ed52bf">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="6848631d1fe8baf973e3d1257f7b50427295477b"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="d7a517f0bde32072f1799e4a47ea34c6d786c287"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="230f11aff069d90d20fc2dc63b48e9ae3d4bdcd1"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="96cdde4b5b5d8d3785b36c3c68cd746aff3005cc"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="7f9ec13a30f1b2cc8bdb1a199b7da54b9ab8860f"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
--- a/b2g/config/emulator-kk/sources.xml
+++ b/b2g/config/emulator-kk/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="276ce45e78b09c4a4ee643646f691d22804754c1">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="6848631d1fe8baf973e3d1257f7b50427295477b"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="d7a517f0bde32072f1799e4a47ea34c6d786c287"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="230f11aff069d90d20fc2dc63b48e9ae3d4bdcd1"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="96cdde4b5b5d8d3785b36c3c68cd746aff3005cc"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="7f9ec13a30f1b2cc8bdb1a199b7da54b9ab8860f"/>
--- a/b2g/config/emulator/sources.xml
+++ b/b2g/config/emulator/sources.xml
@@ -14,17 +14,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="2a165bebfa19b11b697837409f9550dd2917c46c">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="6848631d1fe8baf973e3d1257f7b50427295477b"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="d7a517f0bde32072f1799e4a47ea34c6d786c287"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="230f11aff069d90d20fc2dc63b48e9ae3d4bdcd1"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="3326b51017252e09ccdd715dec6c5e12a7d1ecfe"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="96cdde4b5b5d8d3785b36c3c68cd746aff3005cc"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="7f9ec13a30f1b2cc8bdb1a199b7da54b9ab8860f"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/flame/sources.xml
+++ b/b2g/config/flame/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="cc67f31dc638c0b7edba3cf7e3d87cadf0ed52bf">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="6848631d1fe8baf973e3d1257f7b50427295477b"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="d7a517f0bde32072f1799e4a47ea34c6d786c287"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="230f11aff069d90d20fc2dc63b48e9ae3d4bdcd1"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="96cdde4b5b5d8d3785b36c3c68cd746aff3005cc"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="7f9ec13a30f1b2cc8bdb1a199b7da54b9ab8860f"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="e95b4ce22c825da44d14299e1190ea39a5260bde"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="471afab478649078ad7c75ec6b252481a59e19b8"/>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
         "git_revision": "", 
         "remote": "", 
         "branch": ""
     }, 
-    "revision": "4e3596b10de8df9202d9c0a7f64b7913bdfaaead", 
+    "revision": "c560e79619f1e741655d2e1447cb92469a0419c0", 
     "repo_path": "/integration/gaia-central"
 }
--- a/b2g/config/hamachi/sources.xml
+++ b/b2g/config/hamachi/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="2a165bebfa19b11b697837409f9550dd2917c46c">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="6848631d1fe8baf973e3d1257f7b50427295477b"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="d7a517f0bde32072f1799e4a47ea34c6d786c287"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="230f11aff069d90d20fc2dc63b48e9ae3d4bdcd1"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="96cdde4b5b5d8d3785b36c3c68cd746aff3005cc"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="7f9ec13a30f1b2cc8bdb1a199b7da54b9ab8860f"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
--- a/b2g/config/helix/sources.xml
+++ b/b2g/config/helix/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="2a165bebfa19b11b697837409f9550dd2917c46c">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="6848631d1fe8baf973e3d1257f7b50427295477b"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="d7a517f0bde32072f1799e4a47ea34c6d786c287"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="230f11aff069d90d20fc2dc63b48e9ae3d4bdcd1"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="96cdde4b5b5d8d3785b36c3c68cd746aff3005cc"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
--- a/b2g/config/nexus-4/sources.xml
+++ b/b2g/config/nexus-4/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="cc67f31dc638c0b7edba3cf7e3d87cadf0ed52bf">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="6848631d1fe8baf973e3d1257f7b50427295477b"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="d7a517f0bde32072f1799e4a47ea34c6d786c287"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="230f11aff069d90d20fc2dc63b48e9ae3d4bdcd1"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="96cdde4b5b5d8d3785b36c3c68cd746aff3005cc"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="7f9ec13a30f1b2cc8bdb1a199b7da54b9ab8860f"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
--- a/b2g/config/wasabi/sources.xml
+++ b/b2g/config/wasabi/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="ics_chocolate_rb4.2" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="2a165bebfa19b11b697837409f9550dd2917c46c">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="6848631d1fe8baf973e3d1257f7b50427295477b"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="d7a517f0bde32072f1799e4a47ea34c6d786c287"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="230f11aff069d90d20fc2dc63b48e9ae3d4bdcd1"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="96cdde4b5b5d8d3785b36c3c68cd746aff3005cc"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="7f9ec13a30f1b2cc8bdb1a199b7da54b9ab8860f"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -1036,22 +1036,39 @@ chatbox:-moz-full-screen-ancestor > .cha
   overflow-y: auto;
 }
 
 toolbarpaletteitem[dragover] {
   border-left-color: transparent;
   border-right-color: transparent;
 }
 
+#customization-palette-container {
+  display: flex;
+  flex-direction: column;
+}
+
 #customization-palette:not([hidden]) {
   display: block;
+  flex: 1 1 auto;
   overflow: auto;
   min-height: 3em;
 }
 
+#customization-footer-spacer,
+#customization-spacer {
+  flex: 1 1 auto;
+}
+
+#customization-footer {
+  display: flex;
+  flex-shrink: 0;
+  flex-wrap: wrap;
+}
+
 #customization-toolbar-visibility-button > .box-inherit > .button-menu-dropmarker {
   display: -moz-box;
 }
 
 toolbarpaletteitem[place="palette"] {
   width: 10em;
   height: calc(40px + 2em);
   margin-bottom: 5px;
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -774,16 +774,17 @@ var gBrowserInit = {
 
     // These routines add message listeners. They must run before
     // loading the frame script to ensure that we don't miss any
     // message sent between when the frame script is loaded and when
     // the listener is registered.
     DOMLinkHandler.init();
     gPageStyleMenu.init();
     LanguageDetectionListener.init();
+    BrowserOnClick.init();
 
     let mm = window.getGroupMessageManager("browsers");
     mm.loadFrameScript("chrome://browser/content/content.js", true);
 
     // initialize observers and listeners
     // and give C++ access to gBrowser
     XULBrowserWindow.init();
     window.QueryInterface(Ci.nsIInterfaceRequestor)
@@ -1317,16 +1318,18 @@ var gBrowserInit = {
     PlacesToolbarHelper.uninit();
 
     BookmarkingUI.uninit();
 
     TabsInTitlebar.uninit();
 
     ToolbarIconColor.uninit();
 
+    BrowserOnClick.uninit();
+
     var enumerator = Services.wm.getEnumerator(null);
     enumerator.getNext();
     if (!enumerator.hasMoreElements()) {
       document.persist("sidebar-box", "sidebarcommand");
       document.persist("sidebar-box", "width");
       document.persist("sidebar-box", "src");
       document.persist("sidebar-title", "value");
     }
@@ -2265,84 +2268,102 @@ function SetPageProxyState(aState)
 function PageProxyClickHandler(aEvent)
 {
   if (aEvent.button == 1 && gPrefService.getBoolPref("middlemouse.paste"))
     middleMousePaste(aEvent);
 }
 
 /**
  * Handle command events bubbling up from error page content
- * or from about:newtab
+ * or from about:newtab or from remote error pages that invoke
+ * us via async messaging.
  */
 let BrowserOnClick = {
-  handleEvent: function BrowserOnClick_handleEvent(aEvent) {
-    if (!aEvent.isTrusted || // Don't trust synthetic events
-        aEvent.button == 2) {
+  init: function () {
+    let mm = window.messageManager;
+    mm.addMessageListener("Browser:CertExceptionError", this);
+    mm.addMessageListener("Browser:SiteBlockedError", this);
+    mm.addMessageListener("Browser:NetworkError", this);
+  },
+
+  uninit: function () {
+    let mm = window.messageManager;
+    mm.removeMessageListener("Browser:CertExceptionError", this);
+    mm.removeMessageListener("Browser:SiteBlockedError", this);
+    mm.removeMessageListener("Browser:NetworkError", this);
+  },
+
+  handleEvent: function (event) {
+    if (!event.isTrusted || // Don't trust synthetic events
+        event.button == 2) {
       return;
     }
 
-    let originalTarget = aEvent.originalTarget;
+    let originalTarget = event.originalTarget;
     let ownerDoc = originalTarget.ownerDocument;
 
-    // If the event came from an ssl error page, it is probably either the "Add
-    // Exception…" or "Get me out of here!" button
-    if (ownerDoc.documentURI.startsWith("about:certerror")) {
-      this.onAboutCertError(originalTarget, ownerDoc);
-    }
-    else if (ownerDoc.documentURI.startsWith("about:blocked")) {
-      this.onAboutBlocked(originalTarget, ownerDoc);
-    }
-    else if (ownerDoc.documentURI.startsWith("about:neterror")) {
-      this.onAboutNetError(originalTarget, ownerDoc);
-    }
-    else if (gMultiProcessBrowser &&
-             ownerDoc.documentURI.toLowerCase() == "about:newtab") {
-      this.onE10sAboutNewTab(aEvent, ownerDoc);
+    if (gMultiProcessBrowser &&
+        ownerDoc.documentURI.toLowerCase() == "about:newtab") {
+      this.onE10sAboutNewTab(event, ownerDoc);
     }
     else if (ownerDoc.documentURI.startsWith("about:tabcrashed")) {
-      this.onAboutTabCrashed(aEvent, ownerDoc);
-    }
-  },
-
-  onAboutCertError: function BrowserOnClick_onAboutCertError(aTargetElm, aOwnerDoc) {
-    let elmId = aTargetElm.getAttribute("id");
+      this.onAboutTabCrashed(event, ownerDoc);
+    }
+  },
+
+  receiveMessage: function (msg) {
+    switch (msg.name) {
+      case "Browser:CertExceptionError":
+        this.onAboutCertError(msg.target, msg.json.elementId,
+                              msg.json.isTopFrame, msg.json.location);
+      break;
+      case "Browser:SiteBlockedError":
+        this.onAboutBlocked(msg.json.elementId, msg.json.isMalware,
+                            msg.json.isTopFrame, msg.json.location);
+      break;
+      case "Browser:NetworkError":
+        // Reset network state, the error page will refresh on its own.
+        Services.io.offline = false;
+      break;
+    }
+  },
+
+  onAboutCertError: function (browser, elementId, isTopFrame, location) {
     let secHistogram = Services.telemetry.getHistogramById("SECURITY_UI");
-    let isTopFrame = (aOwnerDoc.defaultView.parent === aOwnerDoc.defaultView);
-
-    let docshell = aOwnerDoc.defaultView.QueryInterface(Ci.nsIInterfaceRequestor)
-                                        .getInterface(Ci.nsIWebNavigation)
-                                        .QueryInterface(Ci.nsIDocShell);
-    let securityInfo = docshell.failedChannel.securityInfo;
-    let sslStatus = securityInfo.QueryInterface(Ci.nsISSLStatusProvider).SSLStatus;
-
-    switch (elmId) {
+
+    switch (elementId) {
       case "exceptionDialogButton":
+        let docshell = aOwnerDoc.defaultView.QueryInterface(Ci.nsIInterfaceRequestor)
+                                            .getInterface(Ci.nsIWebNavigation)
+                                            .QueryInterface(Ci.nsIDocShell);
+        let securityInfo = docshell.failedChannel.securityInfo;
+        let sslStatus = securityInfo.QueryInterface(Ci.nsISSLStatusProvider).SSLStatus;
         if (isTopFrame) {
           secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_BAD_CERT_TOP_CLICK_ADD_EXCEPTION);
         }
         let params = { exceptionAdded : false,
                        sslStatus : sslStatus };
 
         try {
           switch (Services.prefs.getIntPref("browser.ssl_override_behavior")) {
             case 2 : // Pre-fetch & pre-populate
               params.prefetchCert = true;
             case 1 : // Pre-populate
-              params.location = aOwnerDoc.location.href;
+              params.location = location;
           }
         } catch (e) {
           Components.utils.reportError("Couldn't get ssl_override pref: " + e);
         }
 
         window.openDialog('chrome://pippki/content/exceptionDialog.xul',
                           '','chrome,centerscreen,modal', params);
 
         // If the user added the exception cert, attempt to reload the page
         if (params.exceptionAdded) {
-          aOwnerDoc.location.reload();
+          browser.reload();
         }
         break;
 
       case "getMeOutOfHereButton":
         if (isTopFrame) {
           secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_BAD_CERT_TOP_GET_ME_OUT_OF_HERE);
         }
         getMeOutOfHere();
@@ -2358,30 +2379,24 @@ let BrowserOnClick = {
         if (isTopFrame) {
           secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_BAD_CERT_TOP_UNDERSTAND_RISKS);
         }
         break;
 
     }
   },
 
-  onAboutBlocked: function BrowserOnClick_onAboutBlocked(aTargetElm, aOwnerDoc) {
-    let elmId = aTargetElm.getAttribute("id");
+  onAboutBlocked: function (elementId, isMalware, isTopFrame, location) {
+    // Depending on what page we are displaying here (malware/phishing)
+    // use the right strings and links for each.
+    let bucketName = isMalware ? "WARNING_MALWARE_PAGE_":"WARNING_PHISHING_PAGE_";
     let secHistogram = Services.telemetry.getHistogramById("SECURITY_UI");
-
-    // The event came from a button on a malware/phishing block page
-    // First check whether it's malware or phishing, so that we can
-    // use the right strings/links
-    let isMalware = /e=malwareBlocked/.test(aOwnerDoc.documentURI);
-    let bucketName = isMalware ? "WARNING_MALWARE_PAGE_":"WARNING_PHISHING_PAGE_";
     let nsISecTel = Ci.nsISecurityUITelemetry;
-    let isIframe = (aOwnerDoc.defaultView.parent === aOwnerDoc.defaultView);
-    bucketName += isIframe ? "TOP_" : "FRAME_";
-
-    switch (elmId) {
+    bucketName += isTopFrame ? "TOP_" : "FRAME_";
+    switch (elementId) {
       case "getMeOutButton":
         secHistogram.add(nsISecTel[bucketName + "GET_ME_OUT_OF_HERE"]);
         getMeOutOfHere();
         break;
 
       case "reportButton":
         // This is the "Why is this site blocked" button.  For malware,
         // we can fetch a site-specific report, for phishing, we redirect
@@ -2391,17 +2406,17 @@ let BrowserOnClick = {
         // the measurement is for how many users clicked the WHY BLOCKED button
         secHistogram.add(nsISecTel[bucketName + "WHY_BLOCKED"]);
 
         if (isMalware) {
           // Get the stop badware "why is this blocked" report url,
           // append the current url, and go there.
           try {
             let reportURL = formatURL("browser.safebrowsing.malware.reportURL", true);
-            reportURL += aOwnerDoc.location.href;
+            reportURL += location;
             content.location = reportURL;
           } catch (e) {
             Components.utils.reportError("Couldn't get malware report URL: " + e);
           }
         }
         else { // It's a phishing site, not malware
           openHelpLink("phishing-malware", false, "current");
         }
@@ -2415,55 +2430,55 @@ let BrowserOnClick = {
   },
 
   /**
    * This functions prevents navigation from happening directly through the <a>
    * link in about:newtab (which is loaded in the parent and therefore would load
    * the next page also in the parent) and instructs the browser to open the url
    * in the current tab which will make it update the remoteness of the tab.
    */
-  onE10sAboutNewTab: function(aEvent, aOwnerDoc) {
-    let isTopFrame = (aOwnerDoc.defaultView.parent === aOwnerDoc.defaultView);
-    if (!isTopFrame || aEvent.button != 0) {
+  onE10sAboutNewTab: function(event, ownerDoc) {
+    let isTopFrame = (ownerDoc.defaultView.parent === ownerDoc.defaultView);
+    if (!isTopFrame || event.button != 0) {
       return;
     }
 
-    let anchorTarget = aEvent.originalTarget.parentNode;
+    let anchorTarget = event.originalTarget.parentNode;
 
     if (anchorTarget instanceof HTMLAnchorElement &&
         anchorTarget.classList.contains("newtab-link")) {
-      aEvent.preventDefault();
+      event.preventDefault();
       openUILinkIn(anchorTarget.href, "current");
     }
   },
 
   /**
    * The about:tabcrashed can't do window.reload() because that
    * would reload the page but not use a remote browser.
    */
-  onAboutTabCrashed: function(aEvent, aOwnerDoc) {
-    let isTopFrame = (aOwnerDoc.defaultView.parent === aOwnerDoc.defaultView);
+  onAboutTabCrashed: function(event, ownerDoc) {
+    let isTopFrame = (ownerDoc.defaultView.parent === ownerDoc.defaultView);
     if (!isTopFrame) {
       return;
     }
 
-    let button = aEvent.originalTarget;
+    let button = event.originalTarget;
     if (button.id == "tryAgain") {
 #ifdef MOZ_CRASHREPORTER
-      if (aOwnerDoc.getElementById("checkSendReport").checked) {
-        let browser = gBrowser.getBrowserForDocument(aOwnerDoc);
+      if (ownerDoc.getElementById("checkSendReport").checked) {
+        let browser = gBrowser.getBrowserForDocument(ownerDoc);
         TabCrashReporter.submitCrashReport(browser);
       }
 #endif
 
       TabCrashReporter.reloadCrashedTabs();
     }
   },
 
-  ignoreWarningButton: function BrowserOnClick_ignoreWarningButton(aIsMalware) {
+  ignoreWarningButton: function (isMalware) {
     // Allow users to override and continue through to the site,
     // but add a notify bar as a reminder, so that they don't lose
     // track after, e.g., tab switching.
     gBrowser.loadURIWithFlags(content.location.href,
                               nsIWebNavigation.LOAD_FLAGS_BYPASS_CLASSIFIER,
                               null, null, null);
 
     Services.perms.add(makeURI(content.location.href), "safe-browsing",
@@ -2472,17 +2487,17 @@ let BrowserOnClick = {
 
     let buttons = [{
       label: gNavigatorBundle.getString("safebrowsing.getMeOutOfHereButton.label"),
       accessKey: gNavigatorBundle.getString("safebrowsing.getMeOutOfHereButton.accessKey"),
       callback: function() { getMeOutOfHere(); }
     }];
 
     let title;
-    if (aIsMalware) {
+    if (isMalware) {
       title = gNavigatorBundle.getString("safebrowsing.reportedAttackSite");
       buttons[1] = {
         label: gNavigatorBundle.getString("safebrowsing.notAnAttackButton.label"),
         accessKey: gNavigatorBundle.getString("safebrowsing.notAnAttackButton.accessKey"),
         callback: function() {
           openUILinkIn(gSafeBrowsing.getReportURL('MalwareError'), 'tab');
         }
       };
@@ -2511,23 +2526,16 @@ let BrowserOnClick = {
       "chrome://global/skin/icons/blacklist_favicon.png",
       notificationBox.PRIORITY_CRITICAL_HIGH,
       buttons
     );
     // Persist the notification until the user removes so it
     // doesn't get removed on redirects.
     notification.persistence = -1;
   },
-
-  onAboutNetError: function BrowserOnClick_onAboutNetError(aTargetElm, aOwnerDoc) {
-    let elmId = aTargetElm.getAttribute("id");
-    if (elmId != "errorTryAgain" || !/e=netOffline/.test(aOwnerDoc.documentURI))
-      return;
-    Services.io.offline = false;
-  },
 };
 
 /**
  * Re-direct the browser to a known-safe page.  This function is
  * used when, for example, the user browses to a known malware page
  * and is presented with about:blocked.  The "Get me out of here!"
  * button should take the user to the default start page so that even
  * when their own homepage is infected, we can get them somewhere safe.
--- a/browser/base/content/content.js
+++ b/browser/base/content/content.js
@@ -253,48 +253,87 @@ addMessageListener("Finder:Initialize", 
 let ClickEventHandler = {
   init: function init() {
     Cc["@mozilla.org/eventlistenerservice;1"]
       .getService(Ci.nsIEventListenerService)
       .addSystemEventListener(global, "click", this, true);
   },
 
   handleEvent: function(event) {
-    // Bug 903016: Most of this code is an unfortunate duplication from
-    // contentAreaClick in browser.js.
-    if (!event.isTrusted || event.defaultPrevented || event.button == 2)
+    if (!event.isTrusted || event.defaultPrevented || event.button == 2) {
       return;
+    }
+
+    let originalTarget = event.originalTarget;
+    let ownerDoc = originalTarget.ownerDocument;
+
+    // Handle click events from about pages
+    if (ownerDoc.documentURI.startsWith("about:certerror")) {
+      this.onAboutCertError(originalTarget, ownerDoc);
+      return;
+    } else if (ownerDoc.documentURI.startsWith("about:blocked")) {
+      this.onAboutBlocked(originalTarget, ownerDoc);
+      return;
+    } else if (ownerDoc.documentURI.startsWith("about:neterror")) {
+      this.onAboutNetError(originalTarget, ownerDoc);
+    }
 
     let [href, node] = this._hrefAndLinkNodeForClickEvent(event);
 
     let json = { button: event.button, shiftKey: event.shiftKey,
                  ctrlKey: event.ctrlKey, metaKey: event.metaKey,
                  altKey: event.altKey, href: null, title: null,
                  bookmark: false };
 
     if (href) {
       json.href = href;
       if (node) {
         json.title = node.getAttribute("title");
-
         if (event.button == 0 && !event.ctrlKey && !event.shiftKey &&
             !event.altKey && !event.metaKey) {
           json.bookmark = node.getAttribute("rel") == "sidebar";
-          if (json.bookmark)
+          if (json.bookmark) {
             event.preventDefault(); // Need to prevent the pageload.
+          }
         }
       }
 
       sendAsyncMessage("Content:Click", json);
       return;
     }
 
     // This might be middle mouse navigation.
-    if (event.button == 1)
+    if (event.button == 1) {
       sendAsyncMessage("Content:Click", json);
+    }
+  },
+
+  onAboutCertError: function (targetElement, ownerDoc) {
+    sendAsyncMessage("Browser:CertExceptionError", {
+      location: ownerDoc.location.href,
+      elementId: targetElement.getAttribute("id"),
+      isTopFrame: (ownerDoc.defaultView.parent === ownerDoc.defaultView)
+    });
+  },
+
+  onAboutBlocked: function (targetElement, ownerDoc) {
+    sendAsyncMessage("Browser:SiteBlockedError", {
+      location: ownerDoc.location.href,
+      isMalware: /e=malwareBlocked/.test(ownerDoc.documentURI),
+      elementId: targetElement.getAttribute("id"),
+      isTopFrame: (ownerDoc.defaultView.parent === ownerDoc.defaultView)
+    });
+  },
+
+  onAboutNetError: function (targetElement, ownerDoc) {
+    let elmId = targetElement.getAttribute("id");
+    if (elmId != "errorTryAgain" || !/e=netOffline/.test(ownerDoc.documentURI)) {
+      return;
+    }
+    sendSyncMessage("Browser:NetworkError", {});
   },
 
   /**
    * Extracts linkNode and href for the current click target.
    *
    * @param event
    *        The click event.
    * @return [href, linkNode].
--- a/browser/components/customizableui/content/customizeMode.inc.xul
+++ b/browser/components/customizableui/content/customizeMode.inc.xul
@@ -11,36 +11,39 @@
       <label>&customizeMode.menuAndToolbars.empty;</label>
       <label onclick="BrowserOpenAddonsMgr('addons://discovery/');"
              onkeypress="BrowserOpenAddonsMgr('addons://discovery/');"
              id="customization-more-tools"
              class="text-link">
         &customizeMode.menuAndToolbars.emptyLink;
       </label>
     </hbox>
-    <vbox id="customization-palette" class="customization-palette" flex="1"/>
-    <spacer id="customization-spacer" flex="1"/>
+    <vbox id="customization-palette" class="customization-palette"/>
+    <spacer id="customization-spacer"/>
     <hbox id="customization-footer">
 #ifdef CAN_DRAW_IN_TITLEBAR
       <button id="customization-titlebar-visibility-button" class="customizationmode-button"
               label="&customizeMode.titlebar;" type="checkbox"
 #NB: because oncommand fires after click, by the time we've fired, the checkbox binding
 #    will already have switched the button's state, so this is correct:
               oncommand="gCustomizeMode.toggleTitlebar(this.hasAttribute('checked'))"/>
 #endif
       <button id="customization-toolbar-visibility-button" label="&customizeMode.toolbars;" class="customizationmode-button" type="menu">
         <menupopup id="customization-toolbar-menu" onpopupshowing="onViewToolbarsPopupShowing(event)"/>
       </button>
-      <spacer flex="1"/>
+      <spacer id="customization-footer-spacer"/>
       <button id="customization-undo-reset-button"
               class="customizationmode-button"
               hidden="true"
               oncommand="gCustomizeMode.undoReset();"
               label="&undoCmd.label;"/>
-      <button id="customization-reset-button" oncommand="gCustomizeMode.reset();" label="&customizeMode.restoreDefaults;" class="customizationmode-button"/>
+      <button id="customization-reset-button"
+              oncommand="gCustomizeMode.reset();"
+              label="&customizeMode.restoreDefaults;"
+              class="customizationmode-button"/>
     </hbox>
   </vbox>
   <vbox id="customization-panel-container">
     <vbox id="customization-panelWrapper">
       <html:style html:type="text/html" scoped="scoped">
         @import url(chrome://global/skin/popup.css);
       </html:style>
       <box class="panel-arrowbox">
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -4421,20 +4421,16 @@ window > chatbox {
     list-style-image: url("chrome://browser/skin/customizableui/customize-titleBar-toggle@2x.png");
     -moz-image-region: rect(0, 48px, 48px, 0);
   }
 
   #customization-titlebar-visibility-button[checked] {
     -moz-image-region: rect(0, 96px, 48px, 48px);
   }
 
-  #customization-titlebar-visibility-button > .button-box > .button-icon {
-    width: 24px;
-  }
-
   .customization-tipPanel-infoBox {
     background-image: url(chrome://browser/skin/customizableui/info-icon-customizeTip@2x.png);
     background-size: 25px 25px;
   }
 
   .customization-tipPanel-contentImage {
     list-style-image: url(chrome://browser/skin/customizableui/customize-illustration@2x.png);
   }
--- a/browser/themes/shared/customizableui/customizeMode.inc.css
+++ b/browser/themes/shared/customizableui/customizeMode.inc.css
@@ -109,60 +109,64 @@
 
 #main-window:-moz-any([customize-entering],[customize-entered]) #browser-bottombox,
 #customization-footer {
   background-color: rgb(236,236,236);
 }
 
 #customization-footer {
   border-top: 1px solid rgb(221,221,221);
-  padding: 15px;
+  padding: 10px;
 }
 
 .customizationmode-button {
   border: 1px solid rgb(192,192,192);
   border-radius: 3px;
-  margin: 0;
+  margin: 5px;
   padding: 2px 12px;
   background-color: rgb(251,251,251);
   color: rgb(71,71,71);
   box-shadow: 0 1px rgba(255, 255, 255, 0.5),
               inset 0 1px rgba(255, 255, 255, 0.5);
   -moz-appearance: none;
 }
 
 .customizationmode-button[disabled="true"] {
   opacity: .5;
 }
 
+.customizationmode-button > .box-inherit > .box-inherit > .button-icon,
+.customizationmode-button > .button-box > .button-icon {
+  height: 24px;
+}
+
 #customization-titlebar-visibility-button {
   list-style-image: url("chrome://browser/skin/customizableui/customize-titleBar-toggle.png");
   -moz-image-region: rect(0, 24px, 24px, 0);
   padding: 2px 7px;
-  -moz-margin-end: 10px;
 }
 
 #customization-titlebar-visibility-button > .button-box > .button-text {
   /* Sadly, button.css thinks its margins are perfect for everyone. */
   -moz-margin-start: 6px !important;
 }
 
+#customization-titlebar-visibility-button > .button-box > .button-icon {
+  vertical-align: middle;
+}
+
 #customization-titlebar-visibility-button[checked] {
   -moz-image-region: rect(0, 48px, 24px, 24px);
   background-color: rgb(218, 218, 218);
   border-color: rgb(168, 168, 168);
   text-shadow: 0 1px rgb(236, 236, 236);
   box-shadow: 0 1px rgba(255, 255, 255, 0.5),
               inset 0 1px rgb(196, 196, 196);
 }
 
-#customization-undo-reset-button {
-  -moz-margin-end: 10px;
-}
-
 #main-window[customize-entered] #customization-panel-container {
   background-image: url("chrome://browser/skin/customizableui/customizeMode-separatorHorizontal.png"),
                     url("chrome://browser/skin/customizableui/customizeMode-separatorVertical.png"),
                     url("chrome://browser/skin/customizableui/customizeMode-gridTexture.png"),
                     url("chrome://browser/skin/customizableui/background-noise-toolbar.png"),
                     linear-gradient(to bottom, #3e86ce, #3878ba);
   background-position: center top, left center, left top, left top, left top;
   background-repeat: no-repeat, no-repeat, repeat, repeat, no-repeat;
--- a/dom/camera/GonkCameraControl.cpp
+++ b/dom/camera/GonkCameraControl.cpp
@@ -225,26 +225,27 @@ nsGonkCameraControl::SetConfigurationInt
         break;
 
       default:
         MOZ_ASSERT_UNREACHABLE("Unanticipated camera mode in SetConfigurationInternal()");
         rv = NS_ERROR_FAILURE;
         break;
     }
 
-    nsresult rv = Set(CAMERA_PARAM_RECORDINGHINT,
-                      aConfig.mMode == kVideoMode);
+    DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    rv = Set(CAMERA_PARAM_RECORDINGHINT, aConfig.mMode == kVideoMode);
     if (NS_FAILED(rv)) {
       DOM_CAMERA_LOGE("Failed to set recording hint (0x%x)\n", rv);
     }
   }
 
-  DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
-  NS_ENSURE_SUCCESS(rv, rv);
-
   mCurrentConfiguration.mMode = aConfig.mMode;
   mCurrentConfiguration.mRecorderProfile = aConfig.mRecorderProfile;
   if (aConfig.mMode == kVideoMode) {
     mCurrentConfiguration.mPreviewSize = mLastRecorderSize;
   }
 
   OnConfigurationChange();
   return NS_OK;
--- a/dom/camera/GonkCameraParameters.cpp
+++ b/dom/camera/GonkCameraParameters.cpp
@@ -144,54 +144,58 @@ GonkCameraParameters::GonkCameraParamete
   if (!mLock) {
     MOZ_CRASH("Out of memory getting new PRRWLock");
   }
 }
 
 GonkCameraParameters::~GonkCameraParameters()
 {
   MOZ_COUNT_DTOR(GonkCameraParameters);
+  mIsoModeMap.Clear();
   MOZ_ASSERT(mLock, "mLock missing in ~GonkCameraParameters()");
   if (mLock) {
     PR_DestroyRWLock(mLock);
     mLock = nullptr;
   }
 }
 
 nsresult
 GonkCameraParameters::MapIsoToGonk(const nsAString& aIso, nsACString& aIsoOut)
 {
-  if (aIso.EqualsASCII("hjr")) {
-    aIsoOut = "ISO_HJR";
-  } else if (aIso.EqualsASCII("auto")) {
-    aIsoOut = "auto";
-  } else {
-    nsAutoCString v = NS_LossyConvertUTF16toASCII(aIso);
-    unsigned int iso;
-    if (sscanf(v.get(), "%u", &iso) != 1) {
-      return NS_ERROR_INVALID_ARG;
+  nsCString* s;
+  if (mIsoModeMap.Get(aIso, &s)) {
+    if (!s) {
+      DOM_CAMERA_LOGE("ISO mode '%s' maps to null Gonk ISO value\n",
+        NS_LossyConvertUTF16toASCII(aIso).get());
+      return NS_ERROR_FAILURE;
     }
-    aIsoOut = nsPrintfCString("ISO%u", iso);
+
+    aIsoOut = *s;
+    return NS_OK;
   }
 
-  return NS_OK;
+  return NS_ERROR_INVALID_ARG;
 }
 
 nsresult
 GonkCameraParameters::MapIsoFromGonk(const char* aIso, nsAString& aIsoOut)
 {
   if (strcmp(aIso, "ISO_HJR") == 0) {
     aIsoOut.AssignASCII("hjr");
   } else if (strcmp(aIso, "auto") == 0) {
     aIsoOut.AssignASCII("auto");
   } else {
     unsigned int iso;
-    if (sscanf(aIso, "ISO%u", &iso) != 1) {
+    char ignored;
+    // Some camera libraries return ISO modes as "ISO100", others as "100".
+    if (sscanf(aIso, "ISO%u%c", &iso, &ignored) != 1 &&
+        sscanf(aIso, "%u%c", &iso, &ignored) != 1) {
       return NS_ERROR_INVALID_ARG;
     }
+    aIsoOut.Truncate(0);
     aIsoOut.AppendInt(iso);
   }
 
   return NS_OK;
 }
 
 // Any members that need to be initialized on the first parameter pull
 // need to get handled in here.
@@ -234,24 +238,27 @@ GonkCameraParameters::Initialize()
   if (mZoomRatios.Length() == 0) {
     // Always report that we support at least 1.0x zoom.
     *mZoomRatios.AppendElement() = 100;
   }
 
   // The return code from GetListAsArray() doesn't matter. If it fails,
   // the isoModes array will be empty, and the subsequent loop won't
   // execute.
+  nsString s;
   nsTArray<nsCString> isoModes;
   GetListAsArray(CAMERA_PARAM_SUPPORTED_ISOMODES, isoModes);
   for (uint32_t i = 0; i < isoModes.Length(); ++i) {
-    nsString v;
-    rv = MapIsoFromGonk(isoModes[i].get(), v);
-    if (NS_SUCCEEDED(rv)) {
-      *mIsoModes.AppendElement() = v;
+    rv = MapIsoFromGonk(isoModes[i].get(), s);
+    if (NS_FAILED(rv)) {
+      DOM_CAMERA_LOGW("Unrecognized ISO mode value '%s'\n", isoModes[i].get());
+      continue;
     }
+    *mIsoModes.AppendElement() = s;
+    mIsoModeMap.Put(s, new nsCString(isoModes[i]));
   }
 
   mInitialized = true;
   return NS_OK;
 }
 
 // Handle nsAStrings
 nsresult
--- a/dom/camera/GonkCameraParameters.h
+++ b/dom/camera/GonkCameraParameters.h
@@ -18,16 +18,17 @@
 #define DOM_CAMERA_GONKCAMERAPARAMETERS_H
 
 #include <math.h>
 #include "camera/CameraParameters.h"
 #include "nsTArray.h"
 #include "nsString.h"
 #include "AutoRwLock.h"
 #include "nsPrintfCString.h"
+#include "nsClassHashtable.h"
 #include "ICameraControl.h"
 
 namespace mozilla {
 
 class GonkCameraParameters
 {
 public:
   GonkCameraParameters();
@@ -95,16 +96,17 @@ protected:
   bool mInitialized;
 
   // Required internal properties
   double mExposureCompensationStep;
   int32_t mExposureCompensationMinIndex;
   int32_t mExposureCompensationMaxIndex;
   nsTArray<int> mZoomRatios;
   nsTArray<nsString> mIsoModes;
+  nsClassHashtable<nsStringHashKey, nsCString> mIsoModeMap;
 
   // This subclass of android::CameraParameters just gives
   // all of the AOSP getters and setters the same signature.
   class Parameters : public android::CameraParameters
   {
   public:
     using android::CameraParameters::set;
     using android::CameraParameters::get;
--- a/dom/camera/test/test_camera_fake_parameters.html
+++ b/dom/camera/test/test_camera_fake_parameters.html
@@ -115,29 +115,46 @@ var tests = [
     }
   },
   {
     key: "fake-iso",
     prep: function setupFakeIso(test) {
       // we should recognize 'auto', 'hjr', and numeric modes; anything else
       // from the driver is ignored, which this test also verifies.
       test.setFakeParameters(
-        "iso=auto;iso-values=auto,ISO_HJR,ISO100,foo,ISObar,ISO200,ISO400,ISO800,ISO1600",
+        "iso=auto;iso-values=auto,ISO_HJR,ISO100,foo,ISObar,ISO150moz,ISO200,400,ISO800,1600",
         function () {
         run();
       });
     },
     test: function testFakeIso(cam, cap) {
-      // values 'foo' and 'ISObar' should not be included in isoModes
       ok(cap.isoModes.length == 7, "ISO modes length = " + cap.isoModes.length);
+
+      // make sure we're not leaking any unexpected values formats
+      ok(cap.isoModes.indexOf("ISO_HJR") == -1, "ISO mode 'ISO_HJR' does not appear");
+      ok(cap.isoModes.indexOf("_HJR") == -1, "ISO mode '_HJR' does not appear");
+      ok(cap.isoModes.indexOf("HJR") == -1, "ISO mode 'HJR' does not appear");
+      ok(cap.isoModes.indexOf("ISO100") == -1, "ISO mode 'ISO100' does not appear");
+      ok(cap.isoModes.indexOf("ISO200") == -1, "ISO mode 'ISO200' does not appear");
+      ok(cap.isoModes.indexOf("ISO800") == -1, "ISO mode 'ISO800' does not appear");
+
+      // make sure any weird values are dropped entirely
       ok(cap.isoModes.indexOf("foo") == -1, "Unknown ISO mode 'foo' is ignored");
       ok(cap.isoModes.indexOf("ISObar") == -1, "Unknown ISO mode 'ISObar' is ignored");
       ok(cap.isoModes.indexOf("bar") == -1, "Unknown ISO mode 'bar' is ignored");
+      ok(cap.isoModes.indexOf("ISO150moz") == -1, "Unknown ISO mode 'ISO150moz' is ignored");
+      ok(cap.isoModes.indexOf("150moz") == -1, "Unknown ISO mode '150moz' is ignored");
+      ok(cap.isoModes.indexOf("150") == -1, "Unknown ISO mode '150' is ignored");
 
-      // test individual ISO modes
+      // make sure expected values are present
+      [ "auto", "hjr", "100", "200", "400", "800", "1600" ].forEach(function(iso) {
+        ok(cap.isoModes.indexOf(iso) != -1, "ISO mode '" + iso + "' is present");
+      });
+
+      // test setters/getters for individual ISO modes
       cap.isoModes.forEach(function(iso, index) {
         cam.iso = iso;
         ok(cam.iso === iso,
           "ISO[" + index + "] = " + iso + ", cam.iso = " + cam.iso);
       });
 
       next();
     }
@@ -246,17 +263,16 @@ var tests = [
       ok(cam.exposureCompensation == 1.5,
          "exposureCompensation(1.25) = " + cam.exposureCompensation);
       cam.exposureCompensation = -1.24;
       ok(cam.exposureCompensation == -1.0,
          "exposureCompensation(-1.24) = " + cam.exposureCompensation);
       cam.exposureCompensation = -1.25;
       ok(cam.exposureCompensation == -1.5,
          "exposureCompensation(-1.25) = " + cam.exposureCompensation);
-
       // Check out-of-bounds values
       cam.exposureCompensation = cap.minExposureCompensation - 1.0;
       ok(cam.exposureCompensation == cap.minExposureCompensation,
          "exposureCompensation(min - 1.0) = " + cam.exposureCompensation);
       cam.exposureCompensation = cap.maxExposureCompensation + 1.0;
       ok(cam.exposureCompensation == cap.maxExposureCompensation,
          "exposureCompensation(max + 1.0) = " + cam.exposureCompensation);
 
--- a/dom/system/gonk/RadioInterfaceLayer.js
+++ b/dom/system/gonk/RadioInterfaceLayer.js
@@ -1636,19 +1636,17 @@ WorkerMessenger.prototype = {
         extraUint2ndCall:
           libcutils.property_get("ro.moz.ril.extra_int_2nd_call", "false") == "true",
         haveQueryIccLockRetryCount:
           libcutils.property_get("ro.moz.ril.query_icc_count", "false") == "true",
         sendStkProfileDownload:
           libcutils.property_get("ro.moz.ril.send_stk_profile_dl", "false") == "true",
         dataRegistrationOnDemand: RILQUIRKS_DATA_REGISTRATION_ON_DEMAND,
         subscriptionControl: RILQUIRKS_SUBSCRIPTION_CONTROL
-      },
-      rilEmergencyNumbers: libcutils.property_get("ril.ecclist") ||
-                           libcutils.property_get("ro.ril.ecclist")
+      }
     };
 
     this.send(null, "setInitialOptions", options);
   },
 
   setDebugFlag: function(aDebug) {
     let options = { debug: aDebug };
     this.send(null, "setDebugFlag", options);
--- a/dom/system/gonk/ril_worker.js
+++ b/dom/system/gonk/ril_worker.js
@@ -47,19 +47,16 @@ let GLOBAL = this;
 
 if (!this.debug) {
   // Debugging stub that goes nowhere.
   this.debug = function debug(message) {
     dump("RIL Worker: " + message + "\n");
   };
 }
 
-let RIL_EMERGENCY_NUMBERS;
-const DEFAULT_EMERGENCY_NUMBERS = ["112", "911"];
-
 // Timeout value for emergency callback mode.
 const EMERGENCY_CB_MODE_TIMEOUT_MS = 300000;  // 5 mins = 300000 ms.
 
 const ICC_MAX_LINEAR_FIXED_RECORDS = 0xfe;
 
 // MMI match groups
 const MMI_MATCH_GROUP_FULL_MMI = 1;
 const MMI_MATCH_GROUP_MMI_PROCEDURE = 2;
@@ -1550,53 +1547,32 @@ RilObject.prototype = {
    * Cache the request for making an emergency call when radio is off. The
    * request shall include two types of callback functions. 'callback' is
    * called when radio is ready, and 'onerror' is called when turning radio
    * on fails.
    */
   cachedDialRequest : null,
 
   /**
-   * Dial the phone.
+   * Dial a non-emergency number.
    *
    * @param number
    *        String containing the number to dial.
    * @param clirMode
    *        Integer for showing/hidding the caller Id to the called party.
    * @param uusInfo
    *        Integer doing something XXX TODO
    */
-  dial: function(options) {
+  dialNonEmergencyNumber: function(options) {
     let onerror = (function onerror(options, errorMsg) {
       options.success = false;
       options.errorMsg = errorMsg;
       this.sendChromeMessage(options);
     }).bind(this, options);
 
-    if (this._isEmergencyNumber(options.number)) {
-      this.dialEmergencyNumber(options, onerror);
-    } else {
-      if (!this._isCdma) {
-        // TODO: Both dial() and sendMMI() functions should be unified at some
-        // point in the future. In the mean time we handle temporary CLIR MMI
-        // commands through the dial() function. Please see bug 889737.
-        let mmi = this._parseMMI(options.number);
-        if (mmi && this._isTemporaryModeCLIR(mmi)) {
-          options.number = mmi.dialNumber;
-          // In temporary mode, MMI_PROCEDURE_ACTIVATION means allowing CLI
-          // presentation, i.e. CLIR_SUPPRESSION. See TS 22.030, Annex B.
-          options.clirMode = mmi.procedure == MMI_PROCEDURE_ACTIVATION ?
-                             CLIR_SUPPRESSION : CLIR_INVOCATION;
-        }
-      }
-      this.dialNonEmergencyNumber(options, onerror);
-    }
-  },
-
-  dialNonEmergencyNumber: function(options, onerror) {
     if (this.radioState == GECKO_RADIOSTATE_OFF) {
       // Notify error in establishing the call without radio.
       onerror(GECKO_ERROR_RADIO_NOT_AVAILABLE);
       return;
     }
 
     if (this.voiceRegistrationState.emergencyCallsOnly ||
         options.isDialEmergency) {
@@ -1604,55 +1580,79 @@ RilObject.prototype = {
       return;
     }
 
     // Exit emergency callback mode when user dial a non-emergency call.
     if (this._isInEmergencyCbMode) {
       this.exitEmergencyCbMode();
     }
 
-    if (this._isCdma && Object.keys(this.currentCalls).length == 1) {
-      // Make a Cdma 3way call.
-      options.featureStr = options.number;
-      this.sendCdmaFlashCommand(options);
-    } else {
-      options.request = REQUEST_DIAL;
-      this.sendDialRequest(options);
-    }
-  },
-
-  dialEmergencyNumber: function(options, onerror) {
+    if (!this._isCdma) {
+      // TODO: Both dial() and sendMMI() functions should be unified at some
+      // point in the future. In the mean time we handle temporary CLIR MMI
+      // commands through the dial() function. Please see bug 889737.
+      let mmi = this._parseMMI(options.number);
+      if (mmi && this._isTemporaryModeCLIR(mmi)) {
+        options.number = mmi.dialNumber;
+        // In temporary mode, MMI_PROCEDURE_ACTIVATION means allowing CLI
+        // presentation, i.e. CLIR_SUPPRESSION. See TS 22.030, Annex B.
+        options.clirMode = mmi.procedure == MMI_PROCEDURE_ACTIVATION ?
+          CLIR_SUPPRESSION : CLIR_INVOCATION;
+      }
+    }
+
+    options.request = REQUEST_DIAL;
+    this.sendDialRequest(options);
+  },
+
+  /**
+   * Dial an emergency number.
+   *
+   * @param number
+   *        String containing the number to dial.
+   * @param clirMode
+   *        Integer for showing/hidding the caller Id to the called party.
+   * @param uusInfo
+   *        Integer doing something XXX TODO
+   */
+  dialEmergencyNumber: function(options) {
+    let onerror = (function onerror(options, errorMsg) {
+      options.success = false;
+      options.errorMsg = errorMsg;
+      this.sendChromeMessage(options);
+    }).bind(this, options);
+
     options.request = RILQUIRKS_REQUEST_USE_DIAL_EMERGENCY_CALL ?
                       REQUEST_DIAL_EMERGENCY_CALL : REQUEST_DIAL;
     if (this.radioState == GECKO_RADIOSTATE_OFF) {
       if (DEBUG) {
         this.context.debug("Automatically enable radio for an emergency call.");
       }
 
       if (!this.cachedDialRequest) {
         this.cachedDialRequest = {};
       }
       this.cachedDialRequest.onerror = onerror;
       this.cachedDialRequest.callback = this.sendDialRequest.bind(this, options);
       this.setRadioEnabled({enabled: true});
       return;
     }
 
+    this.sendDialRequest(options);
+  },
+
+  sendDialRequest: function(options) {
     if (this._isCdma && Object.keys(this.currentCalls).length == 1) {
       // Make a Cdma 3way call.
       options.featureStr = options.number;
       this.sendCdmaFlashCommand(options);
     } else {
-      this.sendDialRequest(options);
-    }
-  },
-
-  sendDialRequest: function(options) {
-    this.telephonyRequestQueue.push(options.request, this.sendRilRequestDial,
-                                    options);
+      this.telephonyRequestQueue.push(options.request, this.sendRilRequestDial,
+                                      options);
+    }
   },
 
   sendRilRequestDial: function(options) {
     let Buf = this.context.Buf;
     Buf.newParcel(options.request, options);
     Buf.writeString(options.number);
     Buf.writeInt32(options.clirMode || 0);
     Buf.writeInt32(options.uusInfo || 0);
@@ -2412,19 +2412,18 @@ RilObject.prototype = {
   /**
    * Helper to parse short string. TS.22.030 Figure 3.5.3.2.
    */
   _isMMIShortString: function(mmiString) {
     if (mmiString.length > 2) {
       return false;
     }
 
-    if (this._isEmergencyNumber(mmiString)) {
-      return false;
-    }
+    // TODO: Should take care of checking if the string is an emergency number
+    // in Bug 889737. See Bug 1023141 for more background.
 
     // In a call case.
     if (Object.getOwnPropertyNames(this.currentCalls).length > 0) {
       return true;
     }
 
     if ((mmiString.length != 2) || (mmiString.charAt(0) !== '1')) {
       return true;
@@ -3304,48 +3303,28 @@ RilObject.prototype = {
     // Calculate and write Parcel size to 1st mark
     Buf.stopCalOutgoingSize();
 
     Buf.writeInt32(0);
     Buf.sendParcel();
   },
 
   /**
-   * Check a given number against the list of emergency numbers provided by the RIL.
-   *
-   * @param number
-   *        The number to look up.
-   */
-   _isEmergencyNumber: function(number) {
-     // Check ril provided numbers first.
-     let numbers = RIL_EMERGENCY_NUMBERS;
-
-     if (numbers) {
-       numbers = numbers.split(",");
-     } else {
-       // No ecclist system property, so use our own list.
-       numbers = DEFAULT_EMERGENCY_NUMBERS;
-     }
-
-     return numbers.indexOf(number) != -1;
-   },
-
-   /**
-    * Checks whether to temporarily suppress caller id for the call.
-    *
-    * @param mmi
-    *        MMI full object.
-    */
-   _isTemporaryModeCLIR: function(mmi) {
-     return (mmi &&
-             mmi.serviceCode == MMI_SC_CLIR &&
-             mmi.dialNumber &&
-             (mmi.procedure == MMI_PROCEDURE_ACTIVATION ||
-              mmi.procedure == MMI_PROCEDURE_DEACTIVATION));
-   },
+   * Checks whether to temporarily suppress caller id for the call.
+   *
+   * @param mmi
+   *        MMI full object.
+   */
+  _isTemporaryModeCLIR: function(mmi) {
+    return (mmi &&
+            mmi.serviceCode == MMI_SC_CLIR &&
+            mmi.dialNumber &&
+            (mmi.procedure == MMI_PROCEDURE_ACTIVATION ||
+             mmi.procedure == MMI_PROCEDURE_DEACTIVATION));
+  },
 
   /**
    * Report STK Service is running.
    */
   reportStkServiceIsRunning: function() {
     this.context.Buf.simpleRequest(REQUEST_REPORT_STK_SERVICE_IS_RUNNING);
   },
 
@@ -4050,16 +4029,17 @@ RilObject.prototype = {
       let options = this.pendingMO.options;
       this.pendingMO = null;
 
       // Find the callIndex of the new outgoing call.
       let callIndex = -1;
       for (let i in newCalls) {
         if (newCalls[i].state !== CALL_STATE_INCOMING) {
           callIndex = newCalls[i].callIndex;
+          newCalls[i].isEmergency = options.isEmergency;
           break;
         }
       }
 
       if (callIndex === -1) {
         // The call doesn't exist.
         options.success = false;
         options.errorMsg = GECKO_CALL_ERROR_UNSPECIFIED;
@@ -4100,19 +4080,19 @@ RilObject.prototype = {
     }
 
     if (newCall.state == CALL_STATE_INCOMING) {
       newCall.isOutgoing = false;
     } else if (newCall.state == CALL_STATE_DIALING) {
       newCall.isOutgoing = true;
     }
 
-    // Set flag for outgoing emergency call.
-    newCall.isEmergency = newCall.isOutgoing &&
-      this._isEmergencyNumber(newCall.number);
+    if (newCall.isEmergency === undefined) {
+      newCall.isEmergency = false;
+    }
 
     // Set flag for conference.
     newCall.isConference = newCall.isMpty ? true : false;
 
     // Add to our map.
     if (newCall.isMpty) {
       this.currentConference.participants[newCall.callIndex] = newCall;
     }
@@ -14963,17 +14943,16 @@ let ContextPool = {
       }
       return;
     }
     method.call(this, aMessage);
   },
 
   setInitialOptions: function(aOptions) {
     DEBUG = DEBUG_WORKER || aOptions.debug;
-    RIL_EMERGENCY_NUMBERS = aOptions.rilEmergencyNumbers;
 
     let quirks = aOptions.quirks;
     RILQUIRKS_CALLSTATE_EXTRA_UINT32 = quirks.callstateExtraUint32;
     RILQUIRKS_V5_LEGACY = quirks.v5Legacy;
     RILQUIRKS_REQUEST_USE_DIAL_EMERGENCY_CALL = quirks.requestUseDialEmergencyCall;
     RILQUIRKS_SIM_APP_STATE_EXTRA_FIELDS = quirks.simAppStateExtraFields;
     RILQUIRKS_EXTRA_UINT32_2ND_CALL = quirks.extraUint2ndCall;
     RILQUIRKS_HAVE_QUERY_ICC_LOCK_RETRY_COUNT = quirks.haveQueryIccLockRetryCount;
--- a/dom/system/gonk/tests/test_ril_worker_ecm.js
+++ b/dom/system/gonk/tests/test_ril_worker_ecm.js
@@ -117,18 +117,19 @@ add_test(function test_request_exit_emer
   do_check_eq(context.RIL._exitEmergencyCbModeTimeoutID, TIMER_ID);
 
   let parcelTypes = [];
   context.Buf.newParcel = function(type, options) {
     parcelTypes.push(type);
   };
 
   // Dial non-emergency call.
-  context.RIL.dial({number: "0912345678",
-                  isDialEmergency: false});
+  context.RIL.dialNonEmergencyNumber({number: "0912345678",
+                                      isEmergency: false,
+                                      isDialEmergency: false});
 
   // Should clear timeout event.
   do_check_eq(context.RIL._exitEmergencyCbModeTimeoutID, null);
 
   // Check indeed sent out REQUEST_EXIT_EMERGENCY_CALLBACK_MODE.
   do_check_neq(parcelTypes.indexOf(REQUEST_EXIT_EMERGENCY_CALLBACK_MODE), -1);
 
   run_next_test();
--- a/dom/telephony/gonk/TelephonyService.js
+++ b/dom/telephony/gonk/TelephonyService.js
@@ -5,16 +5,17 @@
 
 "use strict";
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/Promise.jsm");
+Cu.import("resource://gre/modules/systemlibs.js");
 
 XPCOMUtils.defineLazyGetter(this, "RIL", function () {
   let obj = {};
   Cu.import("resource://gre/modules/ril_consts.js", obj);
   return obj;
 });
 
 const GONK_TELEPHONYSERVICE_CONTRACTID =
@@ -45,16 +46,18 @@ const AUDIO_STATE_NO_CALL  = 0;
 const AUDIO_STATE_INCOMING = 1;
 const AUDIO_STATE_IN_CALL  = 2;
 const AUDIO_STATE_NAME = [
   "PHONE_STATE_NORMAL",
   "PHONE_STATE_RINGTONE",
   "PHONE_STATE_IN_CALL"
 ];
 
+const DEFAULT_EMERGENCY_NUMBERS = ["112", "911"];
+
 let DEBUG;
 function debug(s) {
   dump("TelephonyService: " + s + "\n");
 }
 
 XPCOMUtils.defineLazyGetter(this, "gAudioManager", function getAudioManager() {
   try {
     return Cc["@mozilla.org/telephony/audiomanager;1"]
@@ -326,16 +329,36 @@ TelephonyService.prototype = {
         this._currentCalls[aClientId][call.callIndex] = call;
       }
 
       return false;
     }).bind(this));
   },
 
   /**
+   * Check a given number against the list of emergency numbers provided by the
+   * RIL.
+   *
+   * @param aNumber
+   *        The number to look up.
+   */
+  _isEmergencyNumber: function(aNumber) {
+    // Check ril provided numbers first.
+    let numbers = libcutils.property_get("ril.ecclist") ||
+                  libcutils.property_get("ro.ril.ecclist");
+    if (numbers) {
+      numbers = numbers.split(",");
+    } else {
+      // No ecclist system property, so use our own list.
+      numbers = DEFAULT_EMERGENCY_NUMBERS;
+    }
+    return numbers.indexOf(aNumber) != -1;
+  },
+
+  /**
    * nsITelephonyService interface.
    */
 
   defaultServiceId: 0,
 
   registerListener: function(aListener) {
     if (this._listeners.indexOf(aListener) >= 0) {
       throw Cr.NS_ERROR_UNEXPECTED;
@@ -483,19 +506,24 @@ TelephonyService.prototype = {
       let parentCall = this._currentCalls[aClientId][childCall.parentId];
       parentCall.childId = CDMA_SECOND_CALL_INDEX;
       parentCall.state = RIL.CALL_STATE_HOLDING;
       parentCall.isSwitchable = false;
       parentCall.isMergeable = true;
       this.notifyCallStateChanged(aClientId, parentCall);
     };
 
+    let isEmergencyNumber = this._isEmergencyNumber(aNumber);
+    let msg = isEmergencyNumber ?
+              "dialEmergencyNumber" :
+              "dialNonEmergencyNumber";
     this.isDialing = true;
-    this._getClient(aClientId).sendWorkerMessage("dial", {
+    this._getClient(aClientId).sendWorkerMessage(msg, {
       number: aNumber,
+      isEmergency: isEmergencyNumber,
       isDialEmergency: aIsEmergency
     }, (function(response) {
       this.isDialing = false;
       if (!response.success) {
         aTelephonyCallback.notifyDialError(response.errorMsg);
         return false;
       }
 
--- a/dom/telephony/test/marionette/head.js
+++ b/dom/telephony/test/marionette/head.js
@@ -9,64 +9,88 @@ const kPrefRilDebuggingEnabled = "ril.de
 
 /**
  * Emulator helper.
  */
 let emulator = (function() {
   let pendingCmdCount = 0;
   let originalRunEmulatorCmd = runEmulatorCmd;
 
+  let pendingShellCount = 0;
+  let originalRunEmulatorShell = runEmulatorShell;
+
   // Overwritten it so people could not call this function directly.
   runEmulatorCmd = function() {
-    throw "Use emulator.runWithCallback(cmd, callback) instead of runEmulatorCmd";
+    throw "Use emulator.runCmdWithCallback(cmd, callback) instead of runEmulatorCmd";
   };
 
-  function run(cmd) {
+  // Overwritten it so people could not call this function directly.
+  runEmulatorShell = function() {
+    throw "Use emulator.runShellCmd(cmd, callback) instead of runEmulatorShell";
+  };
+
+  function runCmd(cmd) {
     let deferred = Promise.defer();
 
     pendingCmdCount++;
     originalRunEmulatorCmd(cmd, function(result) {
       pendingCmdCount--;
       if (result[result.length - 1] === "OK") {
         deferred.resolve(result);
       } else {
         is(result[result.length - 1], "OK", "emulator command result.");
         deferred.reject();
       }
     });
 
     return deferred.promise;
   }
 
-  function runWithCallback(cmd, callback) {
-    run(cmd).then(result => {
+  function runCmdWithCallback(cmd, callback) {
+    runCmd(cmd).then(result => {
       if (callback && typeof callback === "function") {
         callback(result);
       }
     });
   }
 
   /**
    * @return Promise
    */
+  function runShellCmd(aCommands) {
+    let deferred = Promise.defer();
+
+    ++pendingShellCount;
+    originalRunEmulatorShell(aCommands, function(aResult) {
+      --pendingShellCount;
+      deferred.resolve(aResult);
+    });
+
+    return deferred.promise;
+  }
+
+  /**
+   * @return Promise
+   */
   function waitFinish() {
     let deferred = Promise.defer();
 
     waitFor(function() {
       deferred.resolve();
     }, function() {
-      return pendingCmdCount === 0;
+      return pendingCmdCount === 0 && pendingShellCount === 0;
     });
 
     return deferred.promise;
   }
 
   return {
-    run: run,
-    runWithCallback: runWithCallback,
+    runCmd: runCmd,
+    runCmdWithCallback: runCmdWithCallback,
+    runShellCmd: runShellCmd,
     waitFinish: waitFinish
   };
 }());
 
 /**
  * Telephony related helper functions.
  */
 (function() {
@@ -118,17 +142,17 @@ let emulator = (function() {
 
     for (let call of conference.calls) {
       log(".. hangUp " + call.id.number);
       hangUpPromises.push(hangUp(call));
     }
 
     return Promise.all(hangUpPromises)
       .then(() => {
-        return emulator.run("gsm clear");
+        return emulator.runCmd("gsm clear").then(waitForNoCall);
       })
       .then(waitForNoCall);
   }
 
   /**
    * Provide a string with format of the emulator call list result.
    *
    * @param prefix
@@ -356,17 +380,17 @@ let emulator = (function() {
   /**
    * Convenient helper to check the call list existing in the emulator.
    *
    * @param expectedCallList
    *        An array of call info with the format of "callStrPool()[state]".
    * @return A deferred promise.
    */
   function checkEmulatorCallList(expectedCallList) {
-    return emulator.run("gsm list").then(result => {
+    return emulator.runCmd("gsm list").then(result => {
       log("Call list is now: " + result);
       for (let i = 0; i < expectedCallList.length; ++i) {
         is(result[i], expectedCallList[i], "emulator calllist");
       }
     });
   }
 
   /**
@@ -606,17 +630,17 @@ let emulator = (function() {
       checkCallId(number, numberPresentation, name, namePresentation,
                   call.id.number, call.id.name);
       deferred.resolve(call);
     };
 
     numberPresentation = numberPresentation || "";
     name = name || "";
     namePresentation = namePresentation || "";
-    emulator.run("gsm call " + number + "," + numberPresentation + "," + name +
+    emulator.runCmd("gsm call " + number + "," + numberPresentation + "," + name +
                  "," + namePresentation);
     return deferred.promise;
   }
 
   /**
    * Remote party answers the call.
    *
    * @param call
@@ -629,17 +653,17 @@ let emulator = (function() {
     let deferred = Promise.defer();
 
     call.onconnected = function onconnected(event) {
       log("Received 'connected' call event.");
       call.onconnected = null;
       checkEventCallState(event, call, "connected");
       deferred.resolve(call);
     };
-    emulator.run("gsm accept " + call.id.number);
+    emulator.runCmd("gsm accept " + call.id.number);
 
     return deferred.promise;
   }
 
   /**
    * Remote party hangs up the call.
    *
    * @param call
@@ -652,17 +676,17 @@ let emulator = (function() {
     let deferred = Promise.defer();
 
     call.ondisconnected = function ondisconnected(event) {
       log("Received 'disconnected' call event.");
       call.ondisconnected = null;
       checkEventCallState(event, call, "disconnected");
       deferred.resolve(call);
     };
-    emulator.run("gsm cancel " + call.id.number);
+    emulator.runCmd("gsm cancel " + call.id.number);
 
     return deferred.promise;
   }
 
   /**
    * Remote party hangs up all the calls.
    *
    * @param calls
--- a/dom/telephony/test/marionette/test_crash_emulator.js
+++ b/dom/telephony/test/marionette/test_crash_emulator.js
@@ -26,17 +26,17 @@ function answer() {
     // just some code to keep call active for awhile
     callStartTime = Date.now();
     waitFor(cleanUp,function() {
       callDuration = Date.now() - callStartTime;
       log("Waiting while call is active, call duration (ms): " + callDuration);
       return(callDuration >= 2000);
     });
   };
-  emulator.runWithCallback("gsm accept " + outNumber);
+  emulator.runCmdWithCallback("gsm accept " + outNumber);
 }
 
 function cleanUp(){
   outgoingCall.hangUp();
   ok("passed");
   finish();
 }
 
--- a/dom/telephony/test/marionette/test_dsds_connection_conflict.js
+++ b/dom/telephony/test/marionette/test_dsds_connection_conflict.js
@@ -2,17 +2,17 @@
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 MARIONETTE_TIMEOUT = 60000;
 MARIONETTE_HEAD_JS = 'head.js';
 
 function muxModem(id) {
   let deferred = Promise.defer();
 
-  emulator.runWithCallback("mux modem " + id, function() {
+  emulator.runCmdWithCallback("mux modem " + id, function() {
     deferred.resolve();
   });
 
   return deferred.promise;
 }
 
 function testNewCallWhenOtherConnectionInUse(firstServiceId, secondServiceId) {
   log("= testNewCallWhenOtherConnectionInUse =");
--- a/dom/telephony/test/marionette/test_dsds_normal_call.js
+++ b/dom/telephony/test/marionette/test_dsds_normal_call.js
@@ -2,17 +2,17 @@
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 MARIONETTE_TIMEOUT = 60000;
 MARIONETTE_HEAD_JS = 'head.js';
 
 function muxModem(id) {
   let deferred = Promise.defer();
 
-  emulator.runWithCallback("mux modem " + id, function() {
+  emulator.runCmdWithCallback("mux modem " + id, function() {
     deferred.resolve();
   });
 
   return deferred.promise;
 }
 
 function testOutgoingCallForServiceId(number, serviceId) {
   let outCall;
--- a/dom/telephony/test/marionette/test_emergency.js
+++ b/dom/telephony/test/marionette/test_emergency.js
@@ -23,17 +23,17 @@ function dial() {
     is(telephony.calls[0], outgoing);
 
     outgoing.onalerting = function onalerting(event) {
       log("Received 'onalerting' call event.");
       is(outgoing, event.call);
       is(outgoing.state, "alerting");
       is(outgoing.emergency, true);
 
-      emulator.runWithCallback("gsm list", function(result) {
+      emulator.runCmdWithCallback("gsm list", function(result) {
         log("Call list is now: " + result);
         is(result[0], "outbound to  " + number + "        : ringing");
         answer();
       });
     };
   });
 }
 
@@ -44,44 +44,44 @@ function answer() {
 
   outgoing.onconnected = function onconnected(event) {
     log("Received 'connected' call event.");
     is(outgoing, event.call);
     is(outgoing.state, "connected");
 
     is(outgoing, telephony.active);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       is(result[0], "outbound to  " + number + "        : active");
       hangUp();
     });
   };
-  emulator.runWithCallback("gsm accept " + number);
+  emulator.runCmdWithCallback("gsm accept " + number);
 }
 
 function hangUp() {
   log("Hanging up the emergency call.");
 
   // We get no "disconnecting" event when the remote party terminates the call.
 
   outgoing.ondisconnected = function ondisconnected(event) {
     log("Received 'disconnected' call event.");
     is(outgoing, event.call);
     is(outgoing.state, "disconnected");
 
     is(telephony.active, null);
     is(telephony.calls.length, 0);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       cleanUp();
     });
   };
-  emulator.runWithCallback("gsm cancel " + number);
+  emulator.runCmdWithCallback("gsm cancel " + number);
 }
 
 function cleanUp() {
   finish();
 }
 
 startTest(function() {
   dial();
--- a/dom/telephony/test/marionette/test_emergency_badNumber.js
+++ b/dom/telephony/test/marionette/test_emergency_badNumber.js
@@ -11,17 +11,17 @@ function dial() {
 
   telephony.dialEmergency(number).then(null, cause => {
     log("Received promise 'reject'");
 
     is(telephony.active, null);
     is(telephony.calls.length, 0);
     is(cause, "BadNumberError");
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Initial call list: " + result);
       cleanUp();
     });
   });
 }
 
 function cleanUp() {
   finish();
--- a/dom/telephony/test/marionette/test_emergency_label.js
+++ b/dom/telephony/test/marionette/test_emergency_label.js
@@ -1,15 +1,51 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 MARIONETTE_TIMEOUT = 60000;
 MARIONETTE_HEAD_JS = 'head.js';
 
-function testEmergencyLabel(number, emergency) {
+const DEFAULT_ECC_LIST = "112,911";
+
+function setEccListProperty(list) {
+  log("Set property ril.ecclist: " + list);
+
+  let deferred = Promise.defer();
+  try {
+    emulator.runShellCmd(["setprop","ril.ecclist", list]).then(function() {
+      deferred.resolve(list);
+    });
+  } catch (e) {
+    deferred.reject(e);
+  }
+  return deferred.promise;
+}
+
+function getEccListProperty() {
+  log("Get property ril.ecclist.");
+
+  let deferred = Promise.defer();
+  try {
+    emulator.runShellCmd(["getprop","ril.ecclist"]).then(function(aResult) {
+      let list = !aResult.length ? "" : aResult[0];
+      deferred.resolve(list);
+    });
+  } catch (e) {
+    deferred.reject(e);
+  }
+  return deferred.promise;
+}
+
+function testEmergencyLabel(number, list) {
+  if (!list) {
+    list = DEFAULT_ECC_LIST;
+  }
+  let index = list.split(",").indexOf(number);
+  let emergency = index != -1;
   log("= testEmergencyLabel = " + number + " should be " +
       (emergency ? "emergency" : "normal") + " call");
 
   let outCall;
 
   return gDial(number)
     .then(call => { outCall = call; })
     .then(() => {
@@ -18,17 +54,32 @@ function testEmergencyLabel(number, emer
     .then(() => gRemoteAnswer(outCall))
     .then(() => {
       is(outCall.emergency, emergency, "emergency result should be correct");
     })
     .then(() => gRemoteHangUp(outCall));
 }
 
 startTest(function() {
-  testEmergencyLabel("112", true)
-    .then(() => testEmergencyLabel("911", true))
-    .then(() => testEmergencyLabel("0912345678", false))
-    .then(() => testEmergencyLabel("777", false))
-    .then(null, () => {
-      ok(false, 'promise rejects during test.');
+  let origEccList;
+  let eccList;
+
+  getEccListProperty()
+    .then(list => {
+      origEccList = eccList = list;
+    })
+    .then(() => testEmergencyLabel("112", eccList))
+    .then(() => testEmergencyLabel("911", eccList))
+    .then(() => testEmergencyLabel("0912345678", eccList))
+    .then(() => testEmergencyLabel("777", eccList))
+    .then(() => {
+      eccList = "777,119";
+      return setEccListProperty(eccList);
+    })
+    .then(() => testEmergencyLabel("777", eccList))
+    .then(() => testEmergencyLabel("119", eccList))
+    .then(() => testEmergencyLabel("112", eccList))
+    .then(() => setEccListProperty(origEccList))
+    .then(null, error => {
+      ok(false, 'promise rejects during test: ' + error);
     })
     .then(finish);
 });
--- a/dom/telephony/test/marionette/test_incoming_already_connected.js
+++ b/dom/telephony/test/marionette/test_incoming_already_connected.js
@@ -22,17 +22,17 @@ function dial() {
     is(telephony.calls.length, 1);
     is(telephony.calls[0], outgoingCall);
 
     outgoingCall.onalerting = function onalerting(event) {
       log("Received 'onalerting' call event.");
       is(outgoingCall, event.call);
       is(outgoingCall.state, "alerting");
 
-      emulator.runWithCallback("gsm list", function(result) {
+      emulator.runCmdWithCallback("gsm list", function(result) {
         log("Call list is now: " + result);
         is(result[0], "outbound to  " + outNumber + " : ringing");
         answer();
       });
     };
   });
 }
 
@@ -42,31 +42,31 @@ function answer() {
   // We get no "connecting" event when the remote party answers the call.
   outgoingCall.onconnected = function onconnectedOut(event) {
     log("Received 'connected' call event for the original outgoing call.");
 
     is(outgoingCall, event.call);
     is(outgoingCall.state, "connected");
     is(outgoingCall, telephony.active);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       is(result[0], "outbound to  " + outNumber + " : active");
 
       if(!gotOriginalConnected){
         gotOriginalConnected = true;
         simulateIncoming();
       } else {
         // Received connected event for original call multiple times (fail)
         ok(false,
            "Received 'connected' event for original call multiple times");
       }
     });
   };
-  emulator.runWithCallback("gsm accept " + outNumber);
+  emulator.runCmdWithCallback("gsm accept " + outNumber);
 }
 
 // With one connected call already, simulate an incoming call
 function simulateIncoming() {
   log("Simulating an incoming call (with one call already connected).");
 
   telephony.onincoming = function onincoming(event) {
     log("Received 'incoming' call event.");
@@ -75,24 +75,24 @@ function simulateIncoming() {
     is(incomingCall.id.number, inNumber);
     is(incomingCall.state, "incoming");
 
     // Should be two calls now
     is(telephony.calls.length, 2);
     is(telephony.calls[0], outgoingCall);
     is(telephony.calls[1], incomingCall);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       is(result[0], "outbound to  " + outNumber + " : active");
       is(result[1], "inbound from " + inNumber + " : incoming");
       answerIncoming();
     });
   };
-  emulator.runWithCallback("gsm call " + inNumber);
+  emulator.runCmdWithCallback("gsm call " + inNumber);
 }
 
 // Answer incoming call; original outgoing call should be held
 function answerIncoming() {
   log("Answering the incoming call.");
 
   let gotConnecting = false;
   incomingCall.onconnecting = function onconnectingIn(event) {
@@ -106,17 +106,17 @@ function answerIncoming() {
     log("Received 'connected' call event for incoming/2nd call.");
     is(incomingCall, event.call);
     is(incomingCall.state, "connected");
     ok(gotConnecting);
 
     is(incomingCall, telephony.active);
     is(outgoingCall.state, "held");
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       is(result[0], "outbound to  " + outNumber + " : held");
       is(result[1], "inbound from " + inNumber + " : active");
       hangUpOutgoing();
     });
   };
   incomingCall.answer();
 }
@@ -138,17 +138,17 @@ function hangUpOutgoing() {
     is(outgoingCall, event.call);
     is(outgoingCall.state, "disconnected");
     ok(gotDisconnecting);
 
     // Back to one call now
     is(telephony.calls.length, 1);
     is(incomingCall.state, "connected");
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       is(result[0], "inbound from " + inNumber + " : active");
       hangUpIncoming();
     });
   };
   outgoingCall.hangUp();
 }
 
@@ -169,17 +169,17 @@ function hangUpIncoming() {
     is(incomingCall, event.call);
     is(incomingCall.state, "disconnected");
     ok(gotDisconnecting);
 
     // Zero calls left
     is(telephony.active, null);
     is(telephony.calls.length, 0);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       cleanUp();
     });
   };
   incomingCall.hangUp();
 }
 
 function cleanUp() {
--- a/dom/telephony/test/marionette/test_incoming_already_held.js
+++ b/dom/telephony/test/marionette/test_incoming_already_held.js
@@ -22,17 +22,17 @@ function dial() {
     is(telephony.calls.length, 1);
     is(telephony.calls[0], outgoingCall);
 
     outgoingCall.onalerting = function onalerting(event) {
       log("Received 'onalerting' call event.");
       is(outgoingCall, event.call);
       is(outgoingCall.state, "alerting");
 
-      emulator.runWithCallback("gsm list", function(result) {
+      emulator.runCmdWithCallback("gsm list", function(result) {
         log("Call list is now: " + result);
         is(result[0], "outbound to  " + outNumber + " : ringing");
         answer();
       });
     };
   });
 }
 
@@ -42,31 +42,31 @@ function answer() {
   // We get no "connecting" event when the remote party answers the call.
   outgoingCall.onconnected = function onconnectedOut(event) {
     log("Received 'connected' call event for the original outgoing call.");
 
     is(outgoingCall, event.call);
     is(outgoingCall.state, "connected");
     is(outgoingCall, telephony.active);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       is(result[0], "outbound to  " + outNumber + " : active");
 
       if(!gotOriginalConnected){
         gotOriginalConnected = true;
         holdCall();
       } else {
         // Received connected event for original call multiple times (fail)
         ok(false,
            "Received 'connected' event for original call multiple times");
       }
     });
   };
-  emulator.runWithCallback("gsm accept " + outNumber);
+  emulator.runCmdWithCallback("gsm accept " + outNumber);
 }
 
 function holdCall() {
   log("Putting the original (outgoing) call on hold.");
 
   let gotHolding = false;
   outgoingCall.onholding = function onholding(event) {
     log("Received 'holding' call event");
@@ -80,17 +80,17 @@ function holdCall() {
     is(outgoingCall, event.call);
     is(outgoingCall.state, "held");
     ok(gotHolding);
 
     is(telephony.active, null);
     is(telephony.calls.length, 1);
     is(telephony.calls[0], outgoingCall);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       is(result[0], "outbound to  " + outNumber + " : held");
       simulateIncoming();
     });
   };
   outgoingCall.hold();
 }
 
@@ -105,24 +105,24 @@ function simulateIncoming() {
     is(incomingCall.id.number, inNumber);
     is(incomingCall.state, "incoming");
 
     // Should be two calls now
     is(telephony.calls.length, 2);
     is(telephony.calls[0], outgoingCall);
     is(telephony.calls[1], incomingCall);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       is(result[0], "outbound to  " + outNumber + " : held");
       is(result[1], "inbound from " + inNumber + " : incoming");
       answerIncoming();
     });
   };
-  emulator.runWithCallback("gsm call " + inNumber);
+  emulator.runCmdWithCallback("gsm call " + inNumber);
 }
 
 // Answer incoming call; original outgoing call should be held
 function answerIncoming() {
   log("Answering the incoming call.");
 
   let gotConnecting = false;
   incomingCall.onconnecting = function onconnectingIn(event) {
@@ -136,17 +136,17 @@ function answerIncoming() {
     log("Received 'connected' call event for incoming/2nd call.");
     is(incomingCall, event.call);
     is(incomingCall.state, "connected");
     ok(gotConnecting);
 
     is(incomingCall, telephony.active);
     is(outgoingCall.state, "held");
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       is(result[0], "outbound to  " + outNumber + " : held");
       is(result[1], "inbound from " + inNumber + " : active");
       hangUpOutgoing();
     });
   };
   incomingCall.answer();
 }
@@ -168,17 +168,17 @@ function hangUpOutgoing() {
     is(outgoingCall, event.call);
     is(outgoingCall.state, "disconnected");
     ok(gotDisconnecting);
 
     // Back to one call now
     is(telephony.calls.length, 1);
     is(incomingCall.state, "connected");
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       is(result[0], "inbound from " + inNumber + " : active");
       hangUpIncoming();
     });
   };
   outgoingCall.hangUp();
 }
 
@@ -199,17 +199,17 @@ function hangUpIncoming() {
     is(incomingCall, event.call);
     is(incomingCall.state, "disconnected");
     ok(gotDisconnecting);
 
     // Zero calls left
     is(telephony.active, null);
     is(telephony.calls.length, 0);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       cleanUp();
     });
   };
   incomingCall.hangUp();
 }
 
 function cleanUp() {
--- a/dom/telephony/test/marionette/test_incoming_answer_hangup.js
+++ b/dom/telephony/test/marionette/test_incoming_answer_hangup.js
@@ -17,23 +17,23 @@ function simulateIncoming() {
     ok(incoming);
     is(incoming.id.number, number);
     is(incoming.state, "incoming");
 
     //ok(telephony.calls === calls); // bug 717414
     is(telephony.calls.length, 1);
     is(telephony.calls[0], incoming);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       is(result[0], "inbound from " + number + " : incoming");
       answer();
     });
   };
-  emulator.runWithCallback("gsm call " + number);
+  emulator.runCmdWithCallback("gsm call " + number);
 }
 
 function answer() {
   log("Answering the incoming call.");
 
   let gotConnecting = false;
   incoming.onconnecting = function onconnecting(event) {
     log("Received 'connecting' call event.");
@@ -45,17 +45,17 @@ function answer() {
   incoming.onconnected = function onconnected(event) {
     log("Received 'connected' call event.");
     is(incoming, event.call);
     is(incoming.state, "connected");
     ok(gotConnecting);
 
     is(incoming, telephony.active);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       is(result[0], "inbound from " + number + " : active");
       hangUp();
     });
   };
   incoming.answer();
 }
 
@@ -74,17 +74,17 @@ function hangUp() {
     log("Received 'disconnected' call event.");
     is(incoming, event.call);
     is(incoming.state, "disconnected");
     ok(gotDisconnecting);
 
     is(telephony.active, null);
     is(telephony.calls.length, 0);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       cleanUp();
     });
   };
   incoming.hangUp();
 }
 
 function cleanUp() {
--- a/dom/telephony/test/marionette/test_incoming_answer_hangup_oncallschanged.js
+++ b/dom/telephony/test/marionette/test_incoming_answer_hangup_oncallschanged.js
@@ -23,24 +23,24 @@ function simulateIncoming() {
     incoming = event.call;
     ok(incoming);
     is(incoming.id.number, number);
     is(incoming.state, "incoming");
 
     is(telephony.calls.length, 1);
     is(telephony.calls[0], incoming);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       is(result[0], "inbound from " + number + " : incoming");
       answer();
     });
   };
 
-  emulator.runWithCallback("gsm call " + number);
+  emulator.runCmdWithCallback("gsm call " + number);
 }
 
 function answer() {
   log("Answering the incoming call.");
 
   let gotConnecting = false;
   incoming.onconnecting = function onconnecting(event) {
     log("Received 'connecting' call event.");
@@ -55,17 +55,17 @@ function answer() {
   incoming.onconnected = function onconnected(event) {
     log("Received 'connected' call event.");
     is(incoming, event.call);
     is(incoming.state, "connected");
     ok(gotConnecting);
 
     is(incoming, telephony.active);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       is(result[0], "inbound from " + number + " : active");
       hangUp();
     });
   };
   incoming.answer();
 }
 
@@ -104,17 +104,17 @@ function hangUp() {
     is(incoming, event.call);
     is(incoming.state, "disconnected");
     ok(gotDisconnecting);
     ok(gotCallschanged);
 
     is(telephony.active, null);
     is(telephony.calls.length, 0);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       cleanUp();
     });
   };
 
   incoming.hangUp();
 }
 
--- a/dom/telephony/test/marionette/test_incoming_answer_remote_hangup.js
+++ b/dom/telephony/test/marionette/test_incoming_answer_remote_hangup.js
@@ -15,23 +15,23 @@ function simulateIncoming() {
     incomingCall = event.call;
     ok(incomingCall);
     is(incomingCall.id.number, inNumber);
     is(incomingCall.state, "incoming");
 
     is(telephony.calls.length, 1);
     is(telephony.calls[0], incomingCall);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       is(result[0], "inbound from " + inNumber + " : incoming");
       answerIncoming();
     });
   };
-  emulator.runWithCallback("gsm call " + inNumber);
+  emulator.runCmdWithCallback("gsm call " + inNumber);
 }
 
 function answerIncoming() {
   log("Answering the incoming call.");
 
   let gotConnecting = false;
   incomingCall.onconnecting = function onconnectingIn(event) {
     log("Received 'connecting' call event for incoming call.");
@@ -43,17 +43,17 @@ function answerIncoming() {
   incomingCall.onconnected = function onconnectedIn(event) {
     log("Received 'connected' call event for incoming call.");
     is(incomingCall, event.call);
     is(incomingCall.state, "connected");
     ok(gotConnecting);
 
     is(incomingCall, telephony.active);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       is(result[0], "inbound from " + inNumber + " : active");
       remoteHangUp();
     });
   };
   incomingCall.answer();
 }
 
@@ -65,22 +65,22 @@ function remoteHangUp() {
   incomingCall.ondisconnected = function ondisconnected(event) {
     log("Received 'disconnected' call event.");
     is(incomingCall, event.call);
     is(incomingCall.state, "disconnected");
 
     is(telephony.active, null);
     is(telephony.calls.length, 0);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       cleanUp();
     });
   };
-  emulator.runWithCallback("gsm cancel " + inNumber);
+  emulator.runCmdWithCallback("gsm cancel " + inNumber);
 }
 
 function cleanUp() {
   telephony.onincoming = null;
   finish();
 }
 
 startTest(function() {
--- a/dom/telephony/test/marionette/test_incoming_connecting_hangup.js
+++ b/dom/telephony/test/marionette/test_incoming_connecting_hangup.js
@@ -15,23 +15,23 @@ function simulateIncoming() {
     incomingCall = event.call;
     ok(incomingCall);
     is(incomingCall.id.number, inNumber);
     is(incomingCall.state, "incoming");
 
     is(telephony.calls.length, 1);
     is(telephony.calls[0], incomingCall);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       is(result[0], "inbound from " + inNumber + " : incoming");
       answerIncoming();
     });
   };
-  emulator.runWithCallback("gsm call " + inNumber);
+  emulator.runCmdWithCallback("gsm call " + inNumber);
 }
 
 function answerIncoming() {
   log("Answering the incoming call.");
 
   incomingCall.onconnecting = function onconnecting(event) {
     log("Received 'connecting' call event.");
     is(incomingCall, event.call);
@@ -72,17 +72,17 @@ function hangUp() {
     log("Received 'disconnected' call event.");
     is(incomingCall, event.call);
     is(incomingCall.state, "disconnected");
     ok(gotDisconnecting);
 
     is(telephony.active, null);
     is(telephony.calls.length, 0);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       cleanUp();
     });
   };
   incomingCall.hangUp();
 }
 
 function cleanUp() {
--- a/dom/telephony/test/marionette/test_incoming_connecting_remote_hangup.js
+++ b/dom/telephony/test/marionette/test_incoming_connecting_remote_hangup.js
@@ -15,23 +15,23 @@ function simulateIncoming() {
     incomingCall = event.call;
     ok(incomingCall);
     is(incomingCall.id.number, inNumber);
     is(incomingCall.state, "incoming");
 
     is(telephony.calls.length, 1);
     is(telephony.calls[0], incomingCall);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       is(result[0], "inbound from " + inNumber + " : incoming");
       answerIncoming();
     });
   };
-  emulator.runWithCallback("gsm call " + inNumber);
+  emulator.runCmdWithCallback("gsm call " + inNumber);
 }
 
 function answerIncoming() {
   log("Answering the incoming call.");
 
   incomingCall.onconnecting = function onconnecting(event) {
     log("Received 'connecting' call event.");
     is(incomingCall, event.call);
@@ -54,22 +54,22 @@ function remoteHangUp() {
   incomingCall.ondisconnected = function ondisconnected(event) {
     log("Received 'disconnected' call event.");
     is(incomingCall, event.call);
     is(incomingCall.state, "disconnected");
 
     is(telephony.active, null);
     is(telephony.calls.length, 0);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       cleanUp();
     });
   };
-  emulator.runWithCallback("gsm cancel " + inNumber);
+  emulator.runCmdWithCallback("gsm cancel " + inNumber);
 }
 
 function cleanUp() {
   telephony.onincoming = null;
   finish();
 }
 
 startTest(function() {
--- a/dom/telephony/test/marionette/test_incoming_hangup_held.js
+++ b/dom/telephony/test/marionette/test_incoming_hangup_held.js
@@ -15,23 +15,23 @@ function simulateIncoming() {
     incomingCall = event.call;
     ok(incomingCall);
     is(incomingCall.id.number, inNumber);
     is(incomingCall.state, "incoming");
 
     is(telephony.calls.length, 1);
     is(telephony.calls[0], incomingCall);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       is(result[0], "inbound from " + inNumber + " : incoming");
       answerIncoming();
     });
   };
-  emulator.runWithCallback("gsm call " + inNumber);
+  emulator.runCmdWithCallback("gsm call " + inNumber);
 }
 
 function answerIncoming() {
   log("Answering the incoming call.");
 
   let gotConnecting = false;
   incomingCall.onconnecting = function onconnectingIn(event) {
     log("Received 'connecting' call event for incoming call.");
@@ -43,17 +43,17 @@ function answerIncoming() {
   incomingCall.onconnected = function onconnectedIn(event) {
     log("Received 'connected' call event for incoming call.");
     is(incomingCall, event.call);
     is(incomingCall.state, "connected");
     ok(gotConnecting);
 
     is(incomingCall, telephony.active);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       is(result[0], "inbound from " + inNumber + " : active");
       hold();
     });
   };
   incomingCall.answer();
 }
 
@@ -73,17 +73,17 @@ function hold() {
     is(incomingCall, event.call);
     is(incomingCall.state, "held");
     ok(gotHolding);
 
     is(telephony.active, null);
     is(telephony.calls.length, 1);
     is(telephony.calls[0], incomingCall);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       is(result[0], "inbound from " + inNumber + " : held");
       hangUp();
     });
   };
   incomingCall.hold();
 }
 
@@ -102,17 +102,17 @@ function hangUp() {
     log("Received 'disconnected' call event.");
     is(incomingCall, event.call);
     is(incomingCall.state, "disconnected");
     ok(gotDisconnecting);
 
     is(telephony.active, null);
     is(telephony.calls.length, 0);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       cleanUp();
     });
   };
   incomingCall.hangUp();
 }
 
 function cleanUp() {
--- a/dom/telephony/test/marionette/test_incoming_hold_resume.js
+++ b/dom/telephony/test/marionette/test_incoming_hold_resume.js
@@ -16,23 +16,23 @@ function simulateIncoming() {
     incomingCall = event.call;
     ok(incomingCall);
     is(incomingCall.id.number, number);
     is(incomingCall.state, "incoming");
 
     is(telephony.calls.length, 1);
     is(telephony.calls[0], incomingCall);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       is(result[0], "inbound from " + number + " : incoming");
       answer();
     });
   };
-  emulator.runWithCallback("gsm call " + number);
+  emulator.runCmdWithCallback("gsm call " + number);
 }
 
 function answer() {
   log("Answering the incoming call.");
 
   let gotConnecting = false;
   incomingCall.onconnecting = function onconnecting(event) {
     log("Received 'connecting' call event.");
@@ -44,17 +44,17 @@ function answer() {
   incomingCall.onconnected = function onconnected(event) {
     log("Received 'connected' call event.");
     is(incomingCall, event.call);
     is(incomingCall.state, "connected");
     ok(gotConnecting);
 
     is(incomingCall, telephony.active);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       is(result[0], "inbound from " + number + " : active");
       hold();
     });
   };
   incomingCall.answer();
 }
 
@@ -74,17 +74,17 @@ function hold() {
     is(incomingCall, event.call);
     is(incomingCall.state, "held");
     ok(gotHolding);
 
     is(telephony.active, null);
     is(telephony.calls.length, 1);
     is(telephony.calls[0], incomingCall);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       is(result[0], "inbound from " + number + " : held");
       // Wait on hold for a couple of seconds
       log("Pausing 2 seconds while on hold");
       setTimeout(resume, 2000);
     });
   };
   incomingCall.hold();
@@ -106,17 +106,17 @@ function resume() {
     is(incomingCall, event.call);
     is(incomingCall.state, "connected");
     ok(gotResuming);
 
     is(incomingCall, telephony.active);
     is(telephony.calls.length, 1);
     is(telephony.calls[0], incomingCall);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       is(result[0], "inbound from " + number + " : active");
       hangUp();
     });
   };
   incomingCall.resume();
 }
 
@@ -135,17 +135,17 @@ function hangUp() {
     log("Received 'disconnected' call event.");
     is(incomingCall, event.call);
     is(incomingCall.state, "disconnected");
     ok(gotDisconnecting);
 
     is(telephony.active, null);
     is(telephony.calls.length, 0);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       cleanUp();
     });
   };
   incomingCall.hangUp();
 }
 
 function cleanUp() {
--- a/dom/telephony/test/marionette/test_incoming_onstatechange.js
+++ b/dom/telephony/test/marionette/test_incoming_onstatechange.js
@@ -15,23 +15,23 @@ function simulateIncoming() {
     incomingCall = event.call;
     ok(incomingCall);
     is(incomingCall.id.number, inNumber);
     is(incomingCall.state, "incoming");
 
     is(telephony.calls.length, 1);
     is(telephony.calls[0], incomingCall);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       is(result[0], "inbound from " + inNumber + " : incoming");
       answerIncoming();
     });
   };
-  emulator.runWithCallback("gsm call " + inNumber);
+  emulator.runCmdWithCallback("gsm call " + inNumber);
 }
 
 function answerIncoming() {
   log("Answering the incoming call.");
 
   gotConnecting = false;
   incomingCall.onstatechange = function statechangeconnect(event) {
     log("Received 'onstatechange' call event.");
@@ -40,17 +40,17 @@ function answerIncoming() {
       is(incomingCall.state, "connecting");
       gotConnecting = true;
     } else {
       is(incomingCall.state, "connected");
       is(telephony.active, incomingCall);
       is(telephony.calls.length, 1);
       is(telephony.calls[0], incomingCall);
 
-      emulator.runWithCallback("gsm list", function(result) {
+      emulator.runCmdWithCallback("gsm list", function(result) {
         log("Call list is now: " + result);
         is(result[0], "inbound from " + inNumber + " : active");
         hold();
       });
     }
   };
   incomingCall.answer();
 }
@@ -66,17 +66,17 @@ function hold() {
       is(incomingCall.state, "holding");
       gotHolding = true;
     } else {
       is(incomingCall.state, "held");
       is(telephony.active, null);
       is(telephony.calls.length, 1);
       is(telephony.calls[0], incomingCall);
 
-      emulator.runWithCallback("gsm list", function(result) {
+      emulator.runCmdWithCallback("gsm list", function(result) {
         log("Call list is now: " + result);
         is(result[0], "inbound from " + inNumber + " : held");
         resume();
       });
     }
   };
   incomingCall.hold();
 }
@@ -92,17 +92,17 @@ function resume() {
       is(incomingCall.state, "resuming");
       gotResuming = true;
     } else {
       is(incomingCall.state, "connected");
       is(telephony.active, incomingCall);
       is(telephony.calls.length, 1);
       is(telephony.calls[0], incomingCall);
 
-      emulator.runWithCallback("gsm list", function(result) {
+      emulator.runCmdWithCallback("gsm list", function(result) {
         log("Call list is now: " + result);
         is(result[0], "inbound from " + inNumber + " : active");
         hangUp();
       });
     }
   };
   incomingCall.resume();
 }
@@ -117,17 +117,17 @@ function hangUp() {
     if(!gotDisconnecting){
       is(incomingCall.state, "disconnecting");
       gotDisconnecting = true;
     } else {
       is(incomingCall.state, "disconnected");
       is(telephony.active, null);
       is(telephony.calls.length, 0);
 
-      emulator.runWithCallback("gsm list", function(result) {
+      emulator.runCmdWithCallback("gsm list", function(result) {
         log("Call list is now: " + result);
         cleanUp();
       });
     }
   };
   incomingCall.hangUp();
 }
 
--- a/dom/telephony/test/marionette/test_incoming_reject.js
+++ b/dom/telephony/test/marionette/test_incoming_reject.js
@@ -17,23 +17,23 @@ function simulateIncoming() {
     ok(incoming);
     is(incoming.id.number, number);
     is(incoming.state, "incoming");
 
     //ok(telephony.calls === calls); // bug 717414
     is(telephony.calls.length, 1);
     is(telephony.calls[0], incoming);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       is(result[0], "inbound from " + number + " : incoming");
       reject();
     });
   };
-  emulator.runWithCallback("gsm call " + number);
+  emulator.runCmdWithCallback("gsm call " + number);
 }
 
 function reject() {
   log("Reject the incoming call.");
 
   let gotDisconnecting = false;
   incoming.ondisconnecting = function ondisconnecting(event) {
     log("Received 'disconnecting' call event.");
@@ -46,17 +46,17 @@ function reject() {
     log("Received 'disconnected' call event.");
     is(incoming, event.call);
     is(incoming.state, "disconnected");
     ok(gotDisconnecting);
 
     is(telephony.active, null);
     is(telephony.calls.length, 0);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       cleanUp();
     });
   };
   incoming.hangUp();
 }
 
 function cleanUp() {
--- a/dom/telephony/test/marionette/test_incoming_remote_cancel.js
+++ b/dom/telephony/test/marionette/test_incoming_remote_cancel.js
@@ -15,44 +15,44 @@ function simulateIncoming() {
     incomingCall = event.call;
     ok(incomingCall);
     is(incomingCall.id.number, inNumber);
     is(incomingCall.state, "incoming");
 
     is(telephony.calls.length, 1);
     is(telephony.calls[0], incomingCall);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       is(result[0], "inbound from " + inNumber + " : incoming");
       cancelIncoming();
     });
   };
-  emulator.runWithCallback("gsm call " + inNumber);
+  emulator.runCmdWithCallback("gsm call " + inNumber);
 }
 
 function cancelIncoming(){
   log("Remote party cancelling call before it is answered.");
 
   // We get no 'disconnecting' event when remote party cancels/hangs-up call
 
   incomingCall.ondisconnected = function ondisconnected(event) {
     log("Received 'disconnected' call event.");
     is(incomingCall, event.call);
     is(incomingCall.state, "disconnected");
 
     is(telephony.active, null);
     is(telephony.calls.length, 0);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       cleanUp();
     });
   };
-  emulator.runWithCallback("gsm cancel " + inNumber);
+  emulator.runCmdWithCallback("gsm cancel " + inNumber);
 }
 
 function cleanUp() {
   telephony.onincoming = null;
   finish();
 }
 
 startTest(function() {
--- a/dom/telephony/test/marionette/test_incoming_remote_hangup_held.js
+++ b/dom/telephony/test/marionette/test_incoming_remote_hangup_held.js
@@ -15,23 +15,23 @@ function simulateIncoming() {
     incomingCall = event.call;
     ok(incomingCall);
     is(incomingCall.id.number, inNumber);
     is(incomingCall.state, "incoming");
 
     is(telephony.calls.length, 1);
     is(telephony.calls[0], incomingCall);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       is(result[0], "inbound from " + inNumber + " : incoming");
       answerIncoming();
     });
   };
-  emulator.runWithCallback("gsm call " + inNumber);
+  emulator.runCmdWithCallback("gsm call " + inNumber);
 }
 
 function answerIncoming() {
   log("Answering the incoming call.");
 
   let gotConnecting = false;
   incomingCall.onconnecting = function onconnectingIn(event) {
     log("Received 'connecting' call event for incoming call.");
@@ -43,17 +43,17 @@ function answerIncoming() {
   incomingCall.onconnected = function onconnectedIn(event) {
     log("Received 'connected' call event for incoming call.");
     is(incomingCall, event.call);
     is(incomingCall.state, "connected");
     ok(gotConnecting);
 
     is(incomingCall, telephony.active);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       is(result[0], "inbound from " + inNumber + " : active");
       hold();
     });
   };
   incomingCall.answer();
 }
 
@@ -73,17 +73,17 @@ function hold() {
     is(incomingCall, event.call);
     is(incomingCall.state, "held");
     ok(gotHolding);
 
     is(telephony.active, null);
     is(telephony.calls.length, 1);
     is(telephony.calls[0], incomingCall);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       is(result[0], "inbound from " + inNumber + " : held");
       hangUp();
     });
   };
   incomingCall.hold();
 }
 
@@ -95,22 +95,22 @@ function hangUp() {
   incomingCall.ondisconnected = function ondisconnected(event) {
     log("Received 'disconnected' call event.");
     is(incomingCall, event.call);
     is(incomingCall.state, "disconnected");
 
     is(telephony.active, null);
     is(telephony.calls.length, 0);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       cleanUp();
     });
   };
-  emulator.runWithCallback("gsm cancel " + inNumber);
+  emulator.runCmdWithCallback("gsm cancel " + inNumber);
 }
 
 function cleanUp() {
   telephony.onincoming = null;
   finish();
 }
 
 startTest(function() {
--- a/dom/telephony/test/marionette/test_multiple_hold.js
+++ b/dom/telephony/test/marionette/test_multiple_hold.js
@@ -17,23 +17,23 @@ function simulateIncoming() {
     incomingCall = event.call;
     ok(incomingCall);
     is(incomingCall.id.number, inNumber);
     is(incomingCall.state, "incoming");
 
     is(telephony.calls.length, 1);
     is(telephony.calls[0], incomingCall);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       is(result[0], "inbound from " + inNumber + " : incoming");
       answerIncoming();
     });
   };
-  emulator.runWithCallback("gsm call " + inNumber);
+  emulator.runCmdWithCallback("gsm call " + inNumber);
 }
 
 function answerIncoming() {
   log("Answering the incoming call.");
 
   let gotConnecting = false;
   incomingCall.onconnecting = function onconnectingIn(event) {
     log("Received 'connecting' call event for original (incoming) call.");
@@ -44,17 +44,17 @@ function answerIncoming() {
 
   incomingCall.onconnected = function onconnectedIn(event) {
     log("Received 'connected' call event for original (incoming) call.");
     is(incomingCall, event.call);
     is(incomingCall.state, "connected");
     ok(gotConnecting);
     is(incomingCall, telephony.active);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       is(result[0], "inbound from " + inNumber + " : active");
       holdCall();
     });
   };
   incomingCall.answer();
 }
 
@@ -75,17 +75,17 @@ function holdCall() {
     is(incomingCall, event.call);
     is(incomingCall.state, "held");
     ok(gotHolding);
 
     is(telephony.active, null);
     is(telephony.calls.length, 1);
     is(telephony.calls[0], incomingCall);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       is(result[0], "inbound from " + inNumber + " : held");
       dial();
     });
   };
   incomingCall.hold();
 }
 
@@ -103,17 +103,17 @@ function dial() {
     is(telephony.calls[0], incomingCall);
     is(telephony.calls[1], outgoingCall);
 
     outgoingCall.onalerting = function onalerting(event) {
       log("Received 'onalerting' call event.");
       is(outgoingCall, event.call);
       is(outgoingCall.state, "alerting");
 
-      emulator.runWithCallback("gsm list", function(result) {
+      emulator.runCmdWithCallback("gsm list", function(result) {
         log("Call list is now: " + result);
         is(result[0], "inbound from " + inNumber + " : held");
         is(result[1], "outbound to  " + outNumber + " : ringing");
         answerOutgoing();
       });
     };
   });
 }
@@ -124,24 +124,24 @@ function answerOutgoing() {
 
   // We get no "connecting" event when the remote party answers the call.
   outgoingCall.onconnected = function onconnectedOut(event) {
     log("Received 'connected' call event for outgoing/2nd call.");
     is(outgoingCall, event.call);
     is(outgoingCall.state, "connected");
     is(outgoingCall, telephony.active);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       is(result[0], "inbound from " + inNumber + " : held");
       is(result[1], "outbound to  " + outNumber + " : active");
       holdSecondCall();
     });
   };
-  emulator.runWithCallback("gsm accept " + outNumber);
+  emulator.runCmdWithCallback("gsm accept " + outNumber);
 }
 
 // With one held call and one active, hold the active one; expect the first
 // (held) call to automatically become active, and the 2nd call to go on hold
 function holdSecondCall() {
   let firstCallReconnected = false;
   let secondCallHeld = false;
 
@@ -182,17 +182,17 @@ function holdSecondCall() {
 }
 
 function verifyCalls() {
   is(telephony.active, incomingCall);
   is(telephony.calls.length, 2);
   is(telephony.calls[0], incomingCall);
   is(telephony.calls[1], outgoingCall);
 
-  emulator.runWithCallback("gsm list", function(result) {
+  emulator.runCmdWithCallback("gsm list", function(result) {
     log("Call list is now: " + result);
     is(result[0], "inbound from " + inNumber + " : active");
     is(result[1], "outbound to  " + outNumber + " : held");
     hangUpIncoming();
   });
 }
 
 // Hang-up the original incoming call, which is now active
@@ -213,17 +213,17 @@ function hangUpIncoming() {
     is(incomingCall.state, "disconnected");
     ok(gotDisconnecting);
 
     // Now back to one call
     is(telephony.active, null);
     is(telephony.calls.length, 1);
     is(telephony.calls[0], outgoingCall);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       is(result[0], "outbound to  " + outNumber + " : held");
       hangUpOutgoing();
     });
   };
   incomingCall.hangUp();
 }
 
@@ -244,17 +244,17 @@ function hangUpOutgoing() {
     is(outgoingCall, event.call);
     is(outgoingCall.state, "disconnected");
     ok(gotDisconnecting);
 
     // Now no calls
     is(telephony.active, null);
     is(telephony.calls.length, 0);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       cleanUp();
     });
   };
   outgoingCall.hangUp();
 }
 
 function cleanUp() {
--- a/dom/telephony/test/marionette/test_outgoing_already_held.js
+++ b/dom/telephony/test/marionette/test_outgoing_already_held.js
@@ -24,23 +24,23 @@ function simulateIncoming() {
 
     // Wait for emulator to catch up before continuing
     waitFor(verifyCallList,function() {
       return(rcvdEmulatorCallback);
     });
   };
 
   let rcvdEmulatorCallback = false;
-  emulator.runWithCallback("gsm call " + inNumber, function(result) {
+  emulator.runCmdWithCallback("gsm call " + inNumber, function(result) {
     rcvdEmulatorCallback = true;
   });
 }
 
 function verifyCallList(){
-  emulator.runWithCallback("gsm list", function(result) {
+  emulator.runCmdWithCallback("gsm list", function(result) {
     log("Call list is now: " + result);
     is(result[0], "inbound from " + inNumber + " : incoming");
     answerIncoming();
   });
 }
 
 function answerIncoming() {
   log("Answering the incoming call.");
@@ -56,17 +56,17 @@ function answerIncoming() {
   incomingCall.onconnected = function onconnectedIn(event) {
     log("Received 'connected' call event for original (incoming) call.");
     is(incomingCall, event.call);
     is(incomingCall.state, "connected");
     ok(gotConnecting);
 
     is(incomingCall, telephony.active);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       is(result[0], "inbound from " + inNumber + " : active");
       holdCall();
     });
   };
   incomingCall.answer();
 }
 
@@ -87,17 +87,17 @@ function holdCall(){
     is(incomingCall, event.call);
     is(incomingCall.state, "held");
     ok(gotHolding);
 
     is(telephony.active, null);
     is(telephony.calls.length, 1);
     is(telephony.calls[0], incomingCall);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       is(result[0], "inbound from " + inNumber + " : held");
       dial();
     });
   };
   incomingCall.hold();
 }
 
@@ -116,17 +116,17 @@ function dial() {
     is(telephony.calls[0], incomingCall);
     is(telephony.calls[1], outgoingCall);
 
     outgoingCall.onalerting = function onalerting(event) {
       log("Received 'onalerting' call event.");
       is(outgoingCall, event.call);
       is(outgoingCall.state, "alerting");
 
-      emulator.runWithCallback("gsm list", function(result) {
+      emulator.runCmdWithCallback("gsm list", function(result) {
         log("Call list is now: " + result);
         is(result[0], "inbound from " + inNumber + " : held");
         is(result[1], "outbound to  " + outNumber + " : ringing");
         answerOutgoing();
       });
     };
   });
 }
@@ -145,23 +145,23 @@ function answerOutgoing() {
 
     // Wait for emulator to catch up before continuing
     waitFor(checkCallList,function() {
       return(rcvdEmulatorCallback);
     });
   };
 
   let rcvdEmulatorCallback = false;
-  emulator.runWithCallback("gsm accept " + outNumber, function(result) {
+  emulator.runCmdWithCallback("gsm accept " + outNumber, function(result) {
     rcvdEmulatorCallback = true;
   });
 }
 
 function checkCallList(){
-  emulator.runWithCallback("gsm list", function(result) {
+  emulator.runCmdWithCallback("gsm list", function(result) {
     log("Call list is now: " + result);
     is(result[0], "inbound from " + inNumber + " : held");
     is(result[1], "outbound to  " + outNumber + " : active");
     hangUpIncoming();
   });
 }
 
 // Hang-up the original incoming call, which is now held
@@ -182,17 +182,17 @@ function hangUpIncoming() {
     is(incomingCall.state, "disconnected");
     ok(gotDisconnecting);
 
     // Now back to one call
     is(telephony.active, outgoingCall);
     is(telephony.calls.length, 1);
     is(telephony.calls[0], outgoingCall);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       is(result[0], "outbound to  " + outNumber + " : active");
       hangUpOutgoing();
     });
   };
   incomingCall.hangUp();
 }
 
@@ -213,17 +213,17 @@ function hangUpOutgoing() {
     is(outgoingCall, event.call);
     is(outgoingCall.state, "disconnected");
     ok(gotDisconnecting);
 
     // Now no calls
     is(telephony.active, null);
     is(telephony.calls.length, 0);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       cleanUp();
     });
   };
   outgoingCall.hangUp();
 }
 
 function cleanUp() {
--- a/dom/telephony/test/marionette/test_outgoing_answer_hangup.js
+++ b/dom/telephony/test/marionette/test_outgoing_answer_hangup.js
@@ -22,17 +22,17 @@ function dial() {
     is(telephony.calls.length, 1);
     is(telephony.calls[0], outgoing);
 
     outgoing.onalerting = function onalerting(event) {
       log("Received 'onalerting' call event.");
       is(outgoing, event.call);
       is(outgoing.state, "alerting");
 
-      emulator.runWithCallback("gsm list", function(result) {
+      emulator.runCmdWithCallback("gsm list", function(result) {
         log("Call list is now: " + result);
         is(result[0], "outbound to  " + number + " : ringing");
         answer();
       });
     };
   });
 }
 
@@ -43,44 +43,44 @@ function answer() {
 
   outgoing.onconnected = function onconnected(event) {
     log("Received 'connected' call event.");
     is(outgoing, event.call);
     is(outgoing.state, "connected");
 
     is(outgoing, telephony.active);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       is(result[0], "outbound to  " + number + " : active");
       hangUp();
     });
   };
-  emulator.runWithCallback("gsm accept " + number);
+  emulator.runCmdWithCallback("gsm accept " + number);
 }
 
 function hangUp() {
   log("Hanging up the outgoing call.");
 
   // We get no "disconnecting" event when the remote party terminates the call.
 
   outgoing.ondisconnected = function ondisconnected(event) {
     log("Received 'disconnected' call event.");
     is(outgoing, event.call);
     is(outgoing.state, "disconnected");
 
     is(telephony.active, null);
     is(telephony.calls.length, 0);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       cleanUp();
     });
   };
-  emulator.runWithCallback("gsm cancel " + number);
+  emulator.runCmdWithCallback("gsm cancel " + number);
 }
 
 function cleanUp() {
   finish();
 }
 
 startTest(function() {
   dial();
--- a/dom/telephony/test/marionette/test_outgoing_answer_hangup_oncallschanged.js
+++ b/dom/telephony/test/marionette/test_outgoing_answer_hangup_oncallschanged.js
@@ -41,17 +41,17 @@ function dial() {
       cleanUp();
     }
   };
 
   telephony.dial(number);
 }
 
 function checkCallList() {
-  emulator.runWithCallback("gsm list", function(result) {
+  emulator.runCmdWithCallback("gsm list", function(result) {
     log("Call list is now: " + result);
     if (((result[0] == "outbound to  " + number + " : unknown") ||
          (result[0] == "outbound to  " + number + " : dialing"))) {
       answer();
     } else {
       window.setTimeout(checkCallList, 100);
     }
   });
@@ -64,29 +64,29 @@ function answer() {
 
   outgoing.onconnected = function onconnected(event) {
     log("Received 'connected' call event.");
     is(outgoing, event.call);
     is(outgoing.state, "connected");
 
     is(outgoing, telephony.active);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list (after 'connected' event) is now: " + result);
       is(result[0], "outbound to  " + number + " : active");
       hangUp();
     });
   };
-  emulator.runWithCallback("gsm accept " + number);
+  emulator.runCmdWithCallback("gsm accept " + number);
 }
 
 function hangUp() {
   log("Hanging up the outgoing call.");
 
-  emulator.runWithCallback("gsm cancel " + number);
+  emulator.runCmdWithCallback("gsm cancel " + number);
 }
 
 function cleanUp() {
   telephony.oncallschanged = null;
   finish();
 }
 
 startTest(function() {
--- a/dom/telephony/test/marionette/test_outgoing_answer_local_hangup.js
+++ b/dom/telephony/test/marionette/test_outgoing_answer_local_hangup.js
@@ -21,17 +21,17 @@ function dial() {
     is(telephony.calls[0], outgoingCall);
 
     outgoingCall.onalerting = function onalerting(event) {
       log("Received 'alerting' call event.");
 
       is(outgoingCall, event.call);
       is(outgoingCall.state, "alerting");
 
-      emulator.runWithCallback("gsm list", function(result) {
+      emulator.runCmdWithCallback("gsm list", function(result) {
         log("Call list is now: " + result);
         is(result[0], "outbound to  " + outNumber + " : ringing");
         answer();
       });
     };
   });
 }
 
@@ -42,23 +42,23 @@ function answer() {
 
   outgoingCall.onconnected = function onconnected(event) {
     log("Received 'connected' call event.");
     is(outgoingCall, event.call);
     is(outgoingCall.state, "connected");
 
     is(outgoingCall, telephony.active);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       is(result[0], "outbound to  " + outNumber + " : active");
       hangUp();
     });
   };
-  emulator.runWithCallback("gsm accept " + outNumber);
+  emulator.runCmdWithCallback("gsm accept " + outNumber);
 }
 
 function hangUp() {
   log("Hanging up the outgoing call (local hang-up).");
 
   let gotDisconnecting = false;
   outgoingCall.ondisconnecting = function ondisconnectingOut(event) {
     log("Received 'disconnecting' call event.");
@@ -71,17 +71,17 @@ function hangUp() {
     log("Received 'disconnected' call event.");
     is(outgoingCall, event.call);
     is(outgoingCall.state, "disconnected");
     ok(gotDisconnecting);
 
     is(telephony.active, null);
     is(telephony.calls.length, 0);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       cleanUp();
     });
   };
   outgoingCall.hangUp();
 }
 
 function cleanUp() {
--- a/dom/telephony/test/marionette/test_outgoing_answer_radio_off.js
+++ b/dom/telephony/test/marionette/test_outgoing_answer_radio_off.js
@@ -54,17 +54,17 @@ function remoteAnswer(call) {
 
   call.onconnected = function(event) {
     log("Received 'connected' call event.");
     call.onconnected = null;
     is(call, event.call);
     is(call.state, "connected");
     deferred.resolve(call);
   };
-  emulator.runWithCallback("gsm accept " + call.id.number);
+  emulator.runCmdWithCallback("gsm accept " + call.id.number);
 
   return deferred.promise;
 }
 
 function disableRadioAndWaitForCallEvent(call) {
   log("Disable radio and wait for call event.");
 
   let deferred = Promise.defer();
--- a/dom/telephony/test/marionette/test_outgoing_badNumber.js
+++ b/dom/telephony/test/marionette/test_outgoing_badNumber.js
@@ -25,17 +25,17 @@ function dial() {
     is(telephony.calls[0], outgoing);
 
     outgoing.onerror = function onerror(event) {
       log("Received 'error' event.");
       is(event.call, outgoing);
       ok(event.call.error);
       is(event.call.error.name, "BadNumberError");
 
-      emulator.runWithCallback("gsm list", function(result) {
+      emulator.runCmdWithCallback("gsm list", function(result) {
         log("Initial call list: " + result);
         cleanUp();
       });
     };
   });
 }
 
 function cleanUp() {
--- a/dom/telephony/test/marionette/test_outgoing_busy.js
+++ b/dom/telephony/test/marionette/test_outgoing_busy.js
@@ -20,40 +20,40 @@ function dial() {
     is(telephony.calls.length, 1);
     is(telephony.calls[0], outgoing);
 
     outgoing.onalerting = function onalerting(event) {
       log("Received 'onalerting' call event.");
       is(outgoing, event.call);
       is(outgoing.state, "alerting");
 
-      emulator.runWithCallback("gsm list", function(result) {
+      emulator.runCmdWithCallback("gsm list", function(result) {
         log("Call list is now: " + result);
         is(result[0], "outbound to  " + number + " : ringing");
         busy();
       });
     };
   });
 }
 
 function busy() {
   log("The receiver is busy.");
 
   outgoing.onerror = function onerror(event) {
     log("Received 'error' call event.");
     is(outgoing, event.call);
     is(event.call.error.name, "BusyError");
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       cleanUp();
     });
   };
 
-  emulator.runWithCallback("gsm busy " + number);
+  emulator.runCmdWithCallback("gsm busy " + number);
 }
 
 function cleanUp() {
   finish();
 }
 
 startTest(function() {
   dial();
--- a/dom/telephony/test/marionette/test_outgoing_hangup_alerting.js
+++ b/dom/telephony/test/marionette/test_outgoing_hangup_alerting.js
@@ -19,17 +19,17 @@ function dial() {
 
     is(outgoing, telephony.active);
     //ok(telephony.calls === calls); // bug 717414
     is(telephony.calls.length, 1);
     is(telephony.calls[0], outgoing);
 
     outgoing.onalerting = function onalerting(event) {
       log("Received 'alerting' call event.");
-      emulator.runWithCallback("gsm list", function(result) {
+      emulator.runCmdWithCallback("gsm list", function(result) {
         log("Call list is now: " + result);
         is(result[0], "outbound to  " + number + " : ringing");
         hangUp();
       });
     };
   });
 }
 
@@ -49,17 +49,17 @@ function hangUp() {
     log("Received 'disconnected' call event.");
     is(outgoing, event.call);
     is(outgoing.state, "disconnected");
     ok(gotDisconnecting);
 
     is(telephony.active, null);
     is(telephony.calls.length, 0);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       cleanUp();
     });
   };
 
   outgoing.hangUp();
 }
 
--- a/dom/telephony/test/marionette/test_outgoing_hangup_held.js
+++ b/dom/telephony/test/marionette/test_outgoing_hangup_held.js
@@ -21,17 +21,17 @@ function dial() {
     is(telephony.calls.length, 1);
     is(telephony.calls[0], outgoing);
 
     outgoing.onalerting = function onalerting(event) {
       log("Received 'onalerting' call event.");
       is(outgoing, event.call);
       is(outgoing.state, "alerting");
 
-      emulator.runWithCallback("gsm list", function(result) {
+      emulator.runCmdWithCallback("gsm list", function(result) {
         log("Call list is now: " + result);
         is(result[0], "outbound to  " + number + " : ringing");
         answer();
       });
     };
   });
 }
 
@@ -42,23 +42,23 @@ function answer() {
 
   outgoing.onconnected = function onconnected(event) {
     log("Received 'connected' call event.");
     is(outgoing, event.call);
     is(outgoing.state, "connected");
 
     is(outgoing, telephony.active);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       is(result[0], "outbound to  " + number + " : active");
       hold();
     });
   };
-  emulator.runWithCallback("gsm accept " + number);
+  emulator.runCmdWithCallback("gsm accept " + number);
 }
 
 function hold() {
   log("Holding the outgoing call.");
 
   outgoing.onholding = function onholding(event) {
     log("Received 'holding' call event.");
     is(outgoing, event.call);
@@ -70,17 +70,17 @@ function hold() {
   outgoing.onheld = function onheld(event) {
     log("Received 'held' call event.");
     is(outgoing, event.call);
     is(outgoing.state, "held");
 
     is(telephony.active, null);
     is(telephony.calls.length, 1);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       is(result[0], "outbound to  " + number + " : held");
       hangUp();
     });
   };
   outgoing.hold();
 }
 
@@ -99,17 +99,17 @@ function hangUp() {
     log("Received 'disconnected' call event.");
     is(outgoing, event.call);
     is(outgoing.state, "disconnected");
     ok(gotDisconnecting);
 
     is(telephony.active, null);
     is(telephony.calls.length, 0);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       cleanUp();
     });
   };
 
   outgoing.hangUp();
 }
 
--- a/dom/telephony/test/marionette/test_outgoing_hold_resume.js
+++ b/dom/telephony/test/marionette/test_outgoing_hold_resume.js
@@ -21,17 +21,17 @@ function dial() {
     is(telephony.calls.length, 1);
     is(telephony.calls[0], outgoingCall);
 
     outgoingCall.onalerting = function onalerting(event) {
       log("Received 'onalerting' call event.");
       is(outgoingCall, event.call);
       is(outgoingCall.state, "alerting");
 
-      emulator.runWithCallback("gsm list", function(result) {
+      emulator.runCmdWithCallback("gsm list", function(result) {
         log("Call list is now: " + result);
         is(result[0], "outbound to  " + number + " : ringing");
         answer();
       });
     };
   });
 }
 
@@ -42,23 +42,23 @@ function answer() {
 
   outgoingCall.onconnected = function onconnected(event) {
     log("Received 'connected' call event.");
     is(outgoingCall, event.call);
     is(outgoingCall.state, "connected");
 
     is(outgoingCall, telephony.active);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       is(result[0], "outbound to  " + number + " : active");
       hold();
     });
   };
-  emulator.runWithCallback("gsm accept " + number);
+  emulator.runCmdWithCallback("gsm accept " + number);
 }
 
 function hold() {
   log("Putting the call on hold.");
 
   let gotHolding = false;
   outgoingCall.onholding = function onholding(event) {
     log("Received 'holding' call event");
@@ -72,17 +72,17 @@ function hold() {
     is(outgoingCall, event.call);
     is(outgoingCall.state, "held");
     ok(gotHolding);
 
     is(telephony.active, null);
     is(telephony.calls.length, 1);
     is(telephony.calls[0], outgoingCall);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       is(result[0], "outbound to  " + number + " : held");
       // Bug 781604: emulator assertion if outgoing call kept on hold
       // Wait on hold for a couple of seconds
       //log("Pausing 2 seconds while on hold");
       //setTimeout(resume, 2000);
       resume();
     });
@@ -106,17 +106,17 @@ function resume() {
     is(outgoingCall, event.call);
     is(outgoingCall.state, "connected");
     ok(gotResuming);
 
     is(outgoingCall, telephony.active);
     is(telephony.calls.length, 1);
     is(telephony.calls[0], outgoingCall);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       is(result[0], "outbound to  " + number + " : active");
       hangUp();
     });
   };
   outgoingCall.resume();
 }
 
@@ -135,17 +135,17 @@ function hangUp() {
     log("Received 'disconnected' call event.");
     is(outgoingCall, event.call);
     is(outgoingCall.state, "disconnected");
     ok(gotDisconnecting);
 
     is(telephony.active, null);
     is(telephony.calls.length, 0);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       cleanUp();
     });
   };
   outgoingCall.hangUp();
 }
 
 function cleanUp() {
--- a/dom/telephony/test/marionette/test_outgoing_onstatechange.js
+++ b/dom/telephony/test/marionette/test_outgoing_onstatechange.js
@@ -23,17 +23,17 @@ function dial() {
     outgoingCall.onstatechange = function statechangering(event) {
       log("Received 'onstatechange' call event.");
 
       is(outgoingCall, event.call);
       let expectedStates = ["dialing", "alerting"];
       ok(expectedStates.indexOf(event.call.state) != -1);
 
       if (event.call.state == "alerting") {
-        emulator.runWithCallback("gsm list", function(result) {
+        emulator.runCmdWithCallback("gsm list", function(result) {
           log("Call list is now: " + result);
           is(result[0], "outbound to  " + outNumber + " : ringing");
           answer();
         });
       }
     };
   });
 }
@@ -44,23 +44,23 @@ function answer() {
   // We get no "connecting" event when the remote party answers the call.
 
   outgoingCall.onstatechange = function onstatechangeanswer(event) {
     log("Received 'onstatechange' call event.");
     is(outgoingCall, event.call);
     is(outgoingCall.state, "connected");
     is(outgoingCall, telephony.active);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       is(result[0], "outbound to  " + outNumber + " : active");
       hold();
     });
   };
-  emulator.runWithCallback("gsm accept " + outNumber);
+  emulator.runCmdWithCallback("gsm accept " + outNumber);
 }
 
 function hold() {
   log("Putting the call on hold.");
 
   let gotHolding = false;
   outgoingCall.onstatechange = function onstatechangehold(event) {
     log("Received 'onstatechange' call event.");
@@ -69,17 +69,17 @@ function hold() {
       is(outgoingCall.state, "holding");
       gotHolding = true;
     } else {
       is(outgoingCall.state, "held");
       is(telephony.active, null);
       is(telephony.calls.length, 1);
       is(telephony.calls[0], outgoingCall);
 
-      emulator.runWithCallback("gsm list", function(result) {
+      emulator.runCmdWithCallback("gsm list", function(result) {
         log("Call list is now: " + result);
         is(result[0], "outbound to  " + outNumber + " : held");
         resume();
       });
     }
   };
   outgoingCall.hold();
 }
@@ -95,17 +95,17 @@ function resume() {
       is(outgoingCall.state, "resuming");
       gotResuming = true;
     } else {
       is(outgoingCall.state, "connected");
       is(telephony.active, outgoingCall);
       is(telephony.calls.length, 1);
       is(telephony.calls[0], outgoingCall);
 
-      emulator.runWithCallback("gsm list", function(result) {
+      emulator.runCmdWithCallback("gsm list", function(result) {
         log("Call list is now: " + result);
         is(result[0], "outbound to  " + outNumber + " : active");
         hangUp();
       });
     }
   };
   outgoingCall.resume();
 }
@@ -120,17 +120,17 @@ function hangUp() {
     if(!gotDisconnecting){
       is(outgoingCall.state, "disconnecting");
       gotDisconnecting = true;
     } else {
       is(outgoingCall.state, "disconnected");
       is(telephony.active, null);
       is(telephony.calls.length, 0);
 
-      emulator.runWithCallback("gsm list", function(result) {
+      emulator.runCmdWithCallback("gsm list", function(result) {
         log("Call list is now: " + result);
         cleanUp();
       });
     }
   };
   outgoingCall.hangUp();
 }
 
--- a/dom/telephony/test/marionette/test_outgoing_radio_off.js
+++ b/dom/telephony/test/marionette/test_outgoing_radio_off.js
@@ -42,17 +42,17 @@ function dial(number) {
 
   telephony.dial(number).then(null, cause => {
     log("Received promise 'reject'");
 
     is(telephony.active, null);
     is(telephony.calls.length, 0);
     is(cause, "RadioNotAvailable");
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Initial call list: " + result);
 
       setRadioEnabled(true, cleanUp);
     });
   });
 }
 
 function cleanUp() {
--- a/dom/telephony/test/marionette/test_outgoing_reject.js
+++ b/dom/telephony/test/marionette/test_outgoing_reject.js
@@ -20,17 +20,17 @@ function dial() {
     is(telephony.calls.length, 1);
     is(telephony.calls[0], outgoing);
 
     outgoing.onalerting = function onalerting(event) {
       log("Received 'onalerting' call event.");
       is(outgoing, event.call);
       is(outgoing.state, "alerting");
 
-      emulator.runWithCallback("gsm list", function(result) {
+      emulator.runCmdWithCallback("gsm list", function(result) {
         log("Call list is now: " + result);
         is(result[0], "outbound to  " + number + " : ringing");
         reject();
       });
     };
   });
 }
 
@@ -48,23 +48,23 @@ function reject() {
 
     // Wait for emulator to catch up before continuing
     waitFor(verifyCallList,function() {
       return(rcvdEmulatorCallback);
     });
   };
 
   let rcvdEmulatorCallback = false;
-  emulator.runWithCallback("gsm cancel " + number, function(result) {
+  emulator.runCmdWithCallback("gsm cancel " + number, function(result) {
     rcvdEmulatorCallback = true;
   });
 }
 
 function verifyCallList(){
-  emulator.runWithCallback("gsm list", function(result) {
+  emulator.runCmdWithCallback("gsm list", function(result) {
     log("Call list is now: " + result);
     cleanUp();
   });
 }
 
 function cleanUp() {
   finish();
 }
--- a/dom/telephony/test/marionette/test_outgoing_remote_hangup_held.js
+++ b/dom/telephony/test/marionette/test_outgoing_remote_hangup_held.js
@@ -20,17 +20,17 @@ function dial() {
     is(telephony.calls[0], outgoingCall);
 
     outgoingCall.onalerting = function onalerting(event) {
       log("Received 'alerting' call event.");
 
       is(outgoingCall, event.call);
       is(outgoingCall.state, "alerting");
 
-      emulator.runWithCallback("gsm list", function(result) {
+      emulator.runCmdWithCallback("gsm list", function(result) {
         log("Call list is now: " + result);
         is(result[0], "outbound to  " + outNumber + " : ringing");
         answer();
       });
     };
   });
 }
 
@@ -41,23 +41,23 @@ function answer() {
 
   outgoingCall.onconnected = function onconnected(event) {
     log("Received 'connected' call event.");
     is(outgoingCall, event.call);
     is(outgoingCall.state, "connected");
 
     is(outgoingCall, telephony.active);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       is(result[0], "outbound to  " + outNumber + " : active");
       hold();
     });
   };
-  emulator.runWithCallback("gsm accept " + outNumber);
+  emulator.runCmdWithCallback("gsm accept " + outNumber);
 }
 
 function hold() {
   log("Holding the outgoing call.");
 
   outgoingCall.onholding = function onholding(event) {
     log("Received 'holding' call event.");
     is(outgoingCall, event.call);
@@ -70,17 +70,17 @@ function hold() {
     log("Received 'held' call event.");
     is(outgoingCall, event.call);
     is(outgoingCall.state, "held");
 
     is(telephony.active, null);
     is(telephony.calls.length, 1);
     is(telephony.calls[0], outgoingCall);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       is(result[0], "outbound to  " + outNumber + " : held");
       hangUp();
     });
   };
   outgoingCall.hold();
 }
 
@@ -92,22 +92,22 @@ function hangUp() {
   outgoingCall.ondisconnected = function ondisconnected(event) {
     log("Received 'disconnected' call event.");
     is(outgoingCall, event.call);
     is(outgoingCall.state, "disconnected");
 
     is(telephony.active, null);
     is(telephony.calls.length, 0);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       cleanUp();
     });
   };
-  emulator.runWithCallback("gsm cancel " + outNumber);
+  emulator.runCmdWithCallback("gsm cancel " + outNumber);
 }
 
 function cleanUp() {
   finish();
 }
 
 startTest(function() {
   dial();
--- a/dom/telephony/test/marionette/test_redundant_operations.js
+++ b/dom/telephony/test/marionette/test_redundant_operations.js
@@ -15,23 +15,23 @@ function simulateIncoming() {
     incomingCall = event.call;
     ok(incomingCall);
     is(incomingCall.id.number, inNumber);
     is(incomingCall.state, "incoming");
 
     is(telephony.calls.length, 1);
     is(telephony.calls[0], incomingCall);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       is(result[0], "inbound from " + inNumber + " : incoming");
       answerIncoming();
     });
   };
-  emulator.runWithCallback("gsm call " + inNumber);
+  emulator.runCmdWithCallback("gsm call " + inNumber);
 }
 
 function answerIncoming() {
   log("Answering the incoming call.");
 
   let gotConnecting = false;
   incomingCall.onconnecting = function onconnectingIn(event) {
     log("Received 'connecting' call event for incoming call.");
@@ -43,17 +43,17 @@ function answerIncoming() {
   incomingCall.onconnected = function onconnectedIn(event) {
     log("Received 'connected' call event for incoming call.");
     is(incomingCall, event.call);
     is(incomingCall.state, "connected");
     ok(gotConnecting);
 
     is(incomingCall, telephony.active);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       is(result[0], "inbound from " + inNumber + " : active");
       answerAlreadyConnected();
     });
   };
   incomingCall.answer();
 }
 
@@ -95,17 +95,17 @@ function hold() {
     is(incomingCall, event.call);
     is(incomingCall.state, "held");
     ok(gotHolding);
 
     is(telephony.active, null);
     is(telephony.calls.length, 1);
     is(telephony.calls[0], incomingCall);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       is(result[0], "inbound from " + inNumber + " : held");
       holdAlreadyHeld();
     });
   };
   incomingCall.hold();
 }
 
@@ -171,17 +171,17 @@ function resume() {
     is(incomingCall, event.call);
     is(incomingCall.state, "connected");
     ok(gotResuming);
 
     is(incomingCall, telephony.active);
     is(telephony.calls.length, 1);
     is(telephony.calls[0], incomingCall);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       is(result[0], "inbound from " + inNumber + " : active");
       resumeNonHeld();
     });
   };
   incomingCall.resume();
 }
 
@@ -222,17 +222,17 @@ function hangUp() {
     log("Received 'disconnected' call event.");
     is(incomingCall, event.call);
     is(incomingCall.state, "disconnected");
     ok(gotDisconnecting);
 
     is(telephony.active, null);
     is(telephony.calls.length, 0);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       answerDisconnected();
     });
   };
   incomingCall.hangUp();
 }
 
 function answerDisconnected() {
--- a/dom/telephony/test/marionette/test_swap_held_and_active.js
+++ b/dom/telephony/test/marionette/test_swap_held_and_active.js
@@ -24,17 +24,17 @@ function dial() {
       log("Received 'alerting' call event.");
 
       is(outgoingCall, event.call);
       is(outgoingCall.state, "alerting");
       is(outgoingCall, telephony.active);
       is(telephony.calls.length, 1);
       is(telephony.calls[0], outgoingCall);
 
-      emulator.runWithCallback("gsm list", function(result) {
+      emulator.runCmdWithCallback("gsm list", function(result) {
         log("Call list is now: " + result);
         is(result[0], "outbound to  " + outNumber + " : ringing");
         answer();
       });
     };
   });
 }
 
@@ -44,31 +44,31 @@ function answer() {
   // We get no "connecting" event when the remote party answers the call.
   outgoingCall.onconnected = function onconnectedOut(event) {
     log("Received 'connected' call event for the original outgoing call.");
 
     is(outgoingCall, event.call);
     is(outgoingCall.state, "connected");
     is(outgoingCall, telephony.active);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       is(result[0], "outbound to  " + outNumber + " : active");
 
       if(!gotOriginalConnected){
         gotOriginalConnected = true;
         holdCall();
       } else {
         // Received connected event for original call multiple times (fail)
         ok(false,
            "Received 'connected' event for original call multiple times.");
       }
     });
   };
-  emulator.runWithCallback("gsm accept " + outNumber);
+  emulator.runCmdWithCallback("gsm accept " + outNumber);
 }
 
 function holdCall() {
   log("Putting the original (outgoing) call on hold.");
 
   let gotHolding = false;
   outgoingCall.onholding = function onholding(event) {
     log("Received 'holding' call event");
@@ -82,17 +82,17 @@ function holdCall() {
     is(outgoingCall, event.call);
     is(outgoingCall.state, "held");
     ok(gotHolding);
 
     is(telephony.active, null);
     is(telephony.calls.length, 1);
     is(telephony.calls[0], outgoingCall);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       is(result[0], "outbound to  " + outNumber + " : held");
       simulateIncoming();
     });
   };
   outgoingCall.hold();
 }
 
@@ -107,24 +107,24 @@ function simulateIncoming() {
     is(incomingCall.id.number, inNumber);
     is(incomingCall.state, "incoming");
 
     // Should be two calls now
     is(telephony.calls.length, 2);
     is(telephony.calls[0], outgoingCall);
     is(telephony.calls[1], incomingCall);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       is(result[0], "outbound to  " + outNumber + " : held");
       is(result[1], "inbound from " + inNumber + " : incoming");
       answerIncoming();
     });
   };
-  emulator.runWithCallback("gsm call " + inNumber);
+  emulator.runCmdWithCallback("gsm call " + inNumber);
 }
 
 // Answer incoming call; original outgoing call should be held
 function answerIncoming() {
   log("Answering the incoming call.");
 
   let gotConnecting = false;
   incomingCall.onconnecting = function onconnectingIn(event) {
@@ -140,17 +140,17 @@ function answerIncoming() {
     ok(gotConnecting);
 
     is(incomingCall, telephony.active);
 
     // Original outbound call now held, incoming call active
     is(outgoingCall.state, "held");
     is(incomingCall.state, "connected");
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       is(result[0], "outbound to  " + outNumber + " : held");
       is(result[1], "inbound from " + inNumber + " : active");
       swapCalls();
     });
   };
   incomingCall.answer();
 }
@@ -193,17 +193,17 @@ function swapCalls() {
   outgoingCall.resume();
 }
 
 function verifySwap() {
   // Call status reflects swap
   is(outgoingCall.state, "connected");
   is(incomingCall.state, "held");
 
-  emulator.runWithCallback("gsm list", function(result) {
+  emulator.runCmdWithCallback("gsm list", function(result) {
     log("Call list is now: " + result);
     is(result[0], "outbound to  " + outNumber + " : active");
     is(result[1], "inbound from " + inNumber + " : held");
 
     // Begin hang-up
     hangUpOutgoing();
   });
 }
@@ -225,17 +225,17 @@ function hangUpOutgoing() {
     is(outgoingCall, event.call);
     is(outgoingCall.state, "disconnected");
     ok(gotDisconnecting);
 
     // Back to one call now
     is(telephony.calls.length, 1);
     is(incomingCall.state, "held");
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       is(result[0], "inbound from " + inNumber + " : held");
       hangUpIncoming();
     });
   };
   outgoingCall.hangUp();
 }
 
@@ -256,17 +256,17 @@ function hangUpIncoming() {
     is(incomingCall, event.call);
     is(incomingCall.state, "disconnected");
     ok(gotDisconnecting);
 
     // Zero calls left
     is(telephony.active, null);
     is(telephony.calls.length, 0);
 
-    emulator.runWithCallback("gsm list", function(result) {
+    emulator.runCmdWithCallback("gsm list", function(result) {
       log("Call list is now: " + result);
       cleanUp();
     });
   };
   incomingCall.hangUp();
 }
 
 function cleanUp() {
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -607,16 +607,19 @@ main.libs = TOPOBJDIR + '/dist/' + CONFI
 main.res = None
 
 cpe = main.add_classpathentry('src', SRCDIR,
     dstdir='src/org/mozilla/gecko',
     exclude_patterns=['org/mozilla/gecko/tests/**',
         'org/mozilla/gecko/resources/**'])
 if not CONFIG['MOZ_CRASHREPORTER']:
     cpe.exclude_patterns += ['org/mozilla/gecko/CrashReporter.java']
+if not CONFIG['MOZ_NATIVE_DEVICES']:
+    cpe.exclude_patterns += ['org/mozilla/gecko/ChromeCast.java']
+    cpe.exclude_patterns += ['org/mozilla/gecko/MediaPlayerManager.java']
 main.add_classpathentry('generated', OBJDIR + '/generated',
     dstdir='generated')
 main.add_classpathentry('thirdparty', TOPSRCDIR + '/mobile/android/thirdparty',
     dstdir='thirdparty',
     ignore_warnings=True)
 
 resources = add_android_eclipse_library_project('FennecResources')
 resources.package_name = 'org.mozilla.fennec.resources'
--- a/python/mozboot/mozboot/base.py
+++ b/python/mozboot/mozboot/base.py
@@ -68,17 +68,17 @@ We recommend the following tools for ins
 
     pyenv   -- https://github.com/yyuu/pyenv)
     pythonz -- https://github.com/saghul/pythonz
     official installers -- http://www.python.org/
 '''
 
 
 # Upgrade Mercurial older than this.
-MODERN_MERCURIAL_VERSION = StrictVersion('2.5')
+MODERN_MERCURIAL_VERSION = StrictVersion('3.0')
 
 # Upgrade Python older than this.
 MODERN_PYTHON_VERSION = StrictVersion('2.7.3')
 
 
 class BaseBootstrapper(object):
     """Base class for system bootstrappers."""
 
--- a/python/mozversioncontrol/mozversioncontrol/__init__.py
+++ b/python/mozversioncontrol/mozversioncontrol/__init__.py
@@ -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/.
+
+from __future__ import unicode_literals
+
+import os
+import re
+import subprocess
+
+from distutils.version import StrictVersion
+
+def get_hg_version(hg):
+    """Obtain the version of the Mercurial client."""
+
+    env = os.environ.copy()
+    env[b'HGPLAIN'] = b'1'
+
+    info = subprocess.check_output([hg, 'version'], env=env)
+    match = re.search('version ([^\+\)]+)', info)
+    if not match:
+        raise Exception('Unable to identify Mercurial version.')
+
+    return StrictVersion(match.group(1))
--- a/python/mozversioncontrol/mozversioncontrol/repoupdate.py
+++ b/python/mozversioncontrol/mozversioncontrol/repoupdate.py
@@ -2,17 +2,16 @@
 # 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/.
 
 from __future__ import unicode_literals
 
 import os
 import subprocess
 
-
 # The logic here is far from robust. Improvements are welcome.
 
 def update_mercurial_repo(hg, repo, path, revision='default',
     hostfingerprints=None):
     """Ensure a HG repository exists at a path and is up to date."""
     hostfingerprints = hostfingerprints or {}
 
     args = [hg]
--- a/testing/tps/setup.py
+++ b/testing/tps/setup.py
@@ -10,16 +10,17 @@ version = '0.5'
 deps = ['httplib2 >= 0.7.3',
         'mozfile >= 1.1',
         'mozhttpd >= 0.7',
         'mozinfo >= 0.7',
         'mozinstall >= 1.9',
         'mozprocess >= 0.18',
         'mozprofile >= 0.21',
         'mozrunner >= 5.35',
+        'mozversion == 0.6',
        ]
 
 # we only support python 2.6+ right now
 assert sys.version_info[0] == 2
 assert sys.version_info[1] >= 6
 
 setup(name='tps',
       version=version,
--- a/testing/tps/tps/testrunner.py
+++ b/testing/tps/tps/testrunner.py
@@ -9,16 +9,17 @@ import random
 import re
 import tempfile
 import time
 import traceback
 
 from mozhttpd import MozHttpd
 import mozinfo
 from mozprofile import Profile
+import mozversion
 
 from .firefoxrunner import TPSFirefoxRunner
 from .phase import TPSTestPhase
 
 
 class TempFile(object):
     """Class for temporary files that delete themselves when garbage-collected.
     """
@@ -289,17 +290,17 @@ class TPSTestRunner(object):
         result = {
           'PASS': lambda x: ('TEST-PASS', ''),
           'FAIL': lambda x: ('TEST-UNEXPECTED-FAIL', x.rstrip()),
           'unknown': lambda x: ('TEST-UNEXPECTED-FAIL', 'test did not complete')
         } [phase.status](phase.errline)
         logstr = "\n%s | %s%s\n" % (result[0], testname, (' | %s' % result[1] if result[1] else ''))
 
         try:
-            repoinfo = self.firefoxRunner.runner.get_repositoryInfo()
+            repoinfo = mozversion.get_version(self.binary)
         except:
             repoinfo = {}
         apprepo = repoinfo.get('application_repository', '')
         appchangeset = repoinfo.get('application_changeset', '')
 
         # save logdata to a temporary file for posting to the db
         tmplogfile = None
         if logdata:
--- a/toolkit/mozapps/extensions/internal/XPIProvider.jsm
+++ b/toolkit/mozapps/extensions/internal/XPIProvider.jsm
@@ -6641,20 +6641,25 @@ function AddonWrapper(aAddon) {
         return AddonManager.PENDING_INSTALL;
     }
     else if (aAddon.pendingUninstall) {
       // If an add-on is pending uninstall then we ignore any other pending
       // operations
       return AddonManager.PENDING_UNINSTALL;
     }
 
-    if (aAddon.active && isAddonDisabled(aAddon))
-      pending |= AddonManager.PENDING_DISABLE;
-    else if (!aAddon.active && !isAddonDisabled(aAddon))
-      pending |= AddonManager.PENDING_ENABLE;
+    // Extensions have an intentional inconsistency between what the DB says is
+    // enabled and what we say to the ouside world. so we need to cover up that
+    // lie here as well.
+    if (aAddon.type != "experiment") {
+      if (aAddon.active && isAddonDisabled(aAddon))
+        pending |= AddonManager.PENDING_DISABLE;
+      else if (!aAddon.active && !isAddonDisabled(aAddon))
+        pending |= AddonManager.PENDING_ENABLE;
+    }
 
     if (aAddon.pendingUpgrade)
       pending |= AddonManager.PENDING_UPGRADE;
 
     return pending;
   });
 
   this.__defineGetter__("operationsRequiringRestart", function AddonWrapper_operationsRequiringRestartGetter() {
--- a/toolkit/mozapps/extensions/test/xpcshell/test_experiment.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_experiment.js
@@ -19,16 +19,20 @@ add_test(function test_experiment() {
 
         Assert.ok(addon.userDisabled, "Experiments are userDisabled by default.");
         Assert.equal(addon.isActive, false, "Add-on is not active.");
         Assert.equal(addon.updateURL, null, "No updateURL for experiments.");
         Assert.equal(addon.applyBackgroundUpdates, AddonManager.AUTOUPDATE_DISABLE,
                      "Background updates are disabled.");
         Assert.equal(addon.permissions, AddonManager.PERM_CAN_UNINSTALL,
                      "Permissions are minimal.");
+        Assert.ok(!(addon.pendingOperations & AddonManager.PENDING_ENABLE),
+                  "Should not be pending enable");
+        Assert.ok(!(addon.pendingOperations & AddonManager.PENDING_DISABLE),
+                  "Should not be pending disable");
 
         // Setting applyBackgroundUpdates should not work.
         addon.applyBackgroundUpdates = AddonManager.AUTOUPDATE_ENABLE;
         Assert.equal(addon.applyBackgroundUpdates, AddonManager.AUTOUPDATE_DISABLE,
                      "Setting applyBackgroundUpdates shouldn't do anything.");
 
         let noCompatibleCalled = false;
         let noUpdateCalled = false;
@@ -67,16 +71,20 @@ add_test(function test_userDisabledNotPe
 
         Assert.ok("experiment1@tests.mozilla.org" in XPIProvider.bootstrappedAddons,
                   "Experiment add-on listed in XPIProvider bootstrapped list.");
 
         AddonManager.getAddonByID("experiment1@tests.mozilla.org", (addon) => {
           Assert.ok(addon, "Add-on retrieved.");
           Assert.equal(addon.userDisabled, false, "Add-on is still enabled after API retrieve.");
           Assert.ok(addon.isActive, "Add-on is still active.");
+          Assert.ok(!(addon.pendingOperations & AddonManager.PENDING_ENABLE),
+                    "Should not be pending enable");
+          Assert.ok(!(addon.pendingOperations & AddonManager.PENDING_DISABLE),
+                    "Should not be pending disable");
 
           // Now when we restart the manager the add-on should revert state.
           restartManager();
           let persisted = JSON.parse(Services.prefs.getCharPref("extensions.bootstrappedAddons"));
           Assert.ok(!("experiment1@tests.mozilla.org" in persisted),
                     "Experiment add-on not persisted to bootstrappedAddons.");
 
           AddonManager.getAddonByID("experiment1@tests.mozilla.org", (addon) => {
--- a/tools/mercurial/hgsetup/config.py
+++ b/tools/mercurial/hgsetup/config.py
@@ -152,8 +152,22 @@ class MercurialConfig(object):
         if 'defaults' not in self._c:
             self._c['defaults'] = {}
 
         d = self._c['defaults']
         if 'qnew' not in d:
             d['qnew'] = '-U'
         else:
             d['qnew'] = '-U ' + d['qnew']
+
+    def get_bugzilla_credentials(self):
+        if 'bugzilla' not in self._c:
+            return None, None
+
+        b = self._c['bugzilla']
+        return b.get('username', None), b.get('password', None)
+
+    def set_bugzilla_credentials(self, username, password):
+        b = self._c.setdefault('bugzilla', {})
+        if username:
+            b['username'] = username
+        if password:
+            b['password'] = password
--- a/tools/mercurial/hgsetup/wizard.py
+++ b/tools/mercurial/hgsetup/wizard.py
@@ -6,19 +6,22 @@ from __future__ import unicode_literals
 
 import difflib
 import errno
 import os
 import shutil
 import sys
 import which
 
+from distutils.version import StrictVersion
+
 from configobj import ConfigObjError
 from StringIO import StringIO
 
+from mozversioncontrol import get_hg_version
 from mozversioncontrol.repoupdate import (
     update_mercurial_repo,
     update_git_repo,
 )
 
 from .config import (
     HOST_FINGERPRINTS,
     MercurialConfig,
@@ -34,27 +37,45 @@ you whether you want me to make changes:
 want me changing without your permission!
 
 If your config is up-to-date, I'm just going to ensure all 3rd party extensions
 are up to date and you won't have to do anything.
 
 To begin, press the enter/return key.
 '''.strip()
 
+OLDEST_NON_LEGACY_VERSION = StrictVersion('3.0')
+LEGACY_MERCURIAL = '''
+You are running an out of date Mercurial client (%s).
+
+For a faster and better Mercurial experience, we HIGHLY recommend you
+upgrade.
+'''.strip()
+
 MISSING_USERNAME = '''
 You don't have a username defined in your Mercurial config file. In order to
 send patches to Mozilla, you'll need to attach a name and email address. If you
 aren't comfortable giving us your full name, pseudonames are acceptable.
 '''.strip()
 
 BAD_DIFF_SETTINGS = '''
 Mozilla developers produce patches in a standard format, but your Mercurial is
 not configured to produce patches in that format.
 '''.strip()
 
+MQ_INFO = '''
+The mq extension manages patches as separate files. It provides an
+alternative to the recommended bookmark-based development workflow.
+
+If you are a newcomer to Mercurial or are coming from Git, it is
+recommended to avoid mq.
+
+Would you like to activate the mq extension
+'''.strip()
+
 BZEXPORT_INFO = '''
 If you plan on uploading patches to Mozilla, there is an extension called
 bzexport that makes it easy to upload patches from the command line via the
 |hg bzexport| command. More info is available at
 https://hg.mozilla.org/hgcustom/version-control-tools/file/default/hgext/bzexport/README
 
 Would you like to activate bzexport
 '''.strip()
@@ -82,16 +103,35 @@ queue does not set patch author informat
 should be included when uploading for review.
 '''.strip()
 
 FINISHED = '''
 Your Mercurial should now be properly configured and recommended extensions
 should be up to date!
 '''.strip()
 
+REVIEWBOARD_MINIMUM_VERSION = StrictVersion('3.0.1')
+
+REVIEWBOARD_INCOMPATIBLE = '''
+Your Mercurial is too old to use the reviewboard extension, which is necessary
+to conduct code review.
+
+Please upgrade to Mercurial %s or newer to use this extension.
+'''.strip()
+
+MISSING_BUGZILLA_CREDENTIALS = '''
+You do not have your Bugzilla credentials defined in your Mercurial config.
+
+Various extensions make use of your Bugzilla credentials to interface with
+Bugzilla to enrich your development experience.
+
+Bugzilla credentials are optional. If you do not provide them, associated
+functionality will not be enabled or you will be prompted for your
+Bugzilla credentials when they are needed.
+'''.lstrip()
 
 class MercurialSetupWizard(object):
     """Command-line wizard to help users configure Mercurial."""
 
     def __init__(self, state_dir):
         # We use normpath since Mercurial expects the hgrc to use native path
         # separators, but state_dir uses unix style paths even on Windows.
         self.state_dir = os.path.normpath(state_dir)
@@ -120,16 +160,34 @@ class MercurialSetupWizard(object):
             print('Error importing existing Mercurial config!\n'
                   '%s\n'
                   'If using quotes, they must wrap the entire string.' % e)
             return 1
 
         print(INITIAL_MESSAGE)
         raw_input()
 
+        hg_version = get_hg_version(hg)
+        if hg_version < OLDEST_NON_LEGACY_VERSION:
+            print(LEGACY_MERCURIAL % hg_version)
+            print('')
+
+            if os.name == 'nt':
+                print('Please upgrade to the latest MozillaBuild to upgrade '
+                    'your Mercurial install.')
+                print('')
+            else:
+                print('Please run |mach bootstrap| to upgrade your Mercurial '
+                    'install.')
+                print('')
+
+            if not self._prompt_yn('Would you like to continue using an old '
+                'Mercurial version'):
+                return 1
+
         if not c.have_valid_username():
             print(MISSING_USERNAME)
             print('')
 
             name = self._prompt('What is your name?')
             email = self._prompt('What is your email address?')
             c.set_username(name, email)
             print('Updated your username.')
@@ -148,21 +206,37 @@ class MercurialSetupWizard(object):
 
         self.prompt_native_extension(c, 'color',
             'Would you like Mercurial to colorize output to your terminal')
 
         self.prompt_native_extension(c, 'rebase',
             'Would you like to enable the rebase extension to allow you to move'
             ' changesets around (which can help maintain a linear history)')
 
-        self.prompt_native_extension(c, 'mq',
-            'Would you like to activate the mq extension to manage patches')
+        self.prompt_native_extension(c, 'histedit',
+            'Would you like to enable the histedit extension to allow history '
+            'rewriting via the "histedit" command (similar to '
+            '`git rebase -i`)')
+
+        self.prompt_native_extension(c, 'mq', MQ_INFO)
 
         self.prompt_external_extension(c, 'bzexport', BZEXPORT_INFO)
 
+        if 'reviewboard' not in c.extensions:
+            if hg_version < REVIEWBOARD_MINIMUM_VERSION:
+                print(REVIEWBOARD_INCOMPATIBLE % REVIEWBOARD_MINIMUM_VERSION)
+            else:
+                p = os.path.join(self.vcs_tools_dir, 'hgext', 'reviewboard',
+                    'client.py')
+                self.prompt_external_extension(c, 'reviewboard',
+                    'Would you like to enable the reviewboard extension so '
+                    'you can easily initiate code reviews against Mozilla '
+                    'projects',
+                    path=p)
+
         if 'mq' in c.extensions:
             self.prompt_external_extension(c, 'mqext', MQEXT_INFO,
                                            os.path.join(self.ext_dir, 'mqext'))
 
             if 'mqext' in c.extensions:
                 self.update_mercurial_repo(
                     hg,
                     'https://bitbucket.org/sfink/mqext',
@@ -181,16 +255,33 @@ class MercurialSetupWizard(object):
             if not c.have_qnew_currentuser_default():
                 print(QNEWCURRENTUSER_INFO)
                 if self._prompt_yn('Would you like qnew to set patch author by '
                                    'default'):
                     c.ensure_qnew_currentuser_default()
                     print('Configured qnew to set patch author by default.')
                     print('')
 
+        if 'reviewboard' in c.extensions:
+            bzuser, bzpass = c.get_bugzilla_credentials()
+
+            if not bzuser or not bzpass:
+                print(MISSING_BUGZILLA_CREDENTIALS)
+
+            if not bzuser:
+                bzuser = self._prompt('What is your Bugzilla email address?',
+                    allow_empty=True)
+
+            if bzuser and not bzpass:
+                bzpass = self._prompt('What is your Bugzilla password?',
+                    allow_empty=True)
+
+            if bzuser or bzpass:
+                c.set_bugzilla_credentials(bzuser, bzpass)
+
         if self.update_vcs_tools:
             self.update_mercurial_repo(
                 hg,
                 'https://hg.mozilla.org/hgcustom/version-control-tools',
                 self.vcs_tools_dir,
                 'default',
                 'Ensuring version-control-tools is up to date...')
 
@@ -276,25 +367,28 @@ class MercurialSetupWizard(object):
         print('=' * 80)
         print(msg)
         try:
             fn(binary, url, dest, branch, *args, **kwargs)
         finally:
             print('=' * 80)
             print('')
 
-    def _prompt(self, msg):
+    def _prompt(self, msg, allow_empty=False):
         print(msg)
 
         while True:
             response = raw_input()
 
             if response:
                 return response
 
+            if allow_empty:
+                return None
+
             print('You must type something!')
 
     def _prompt_yn(self, msg):
         print('%s? [Y/n]' % msg)
 
         while True:
             choice = raw_input().lower().strip()