Merge mozilla-central to mozilla-inbound
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Thu, 03 Jul 2014 14:16:06 +0200
changeset 212983 1022c98a62d29d3ce9ee3dc1cb0276b79ad09bf3
parent 212982 94f150f5b21fb3e1a4266a0f333751f0c932ab9b (current diff)
parent 212885 ac6960197eb6bdd5501a7ddd23dcda6976a8a9e0 (diff)
child 212984 a481b4bc3ae666ed2e168291cb11fd04d0899064
push id3857
push userraliiev@mozilla.com
push dateTue, 02 Sep 2014 16:39:23 +0000
treeherdermozilla-beta@5638b907b505 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone33.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge 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()