Bug 1037235 - toolkit/loader doesn't check module compatibility r=Mossop
☠☠ backed out by b87f8e6760d9 ☠ ☠
authorErik Vold <evold@mozilla.com>
Sat, 30 Aug 2014 15:31:08 -0700
changeset 224246 cd821c1ef789502e97828929b220592880f404eb
parent 224245 afb1867dba70b7274907529dfc6085dc6f26b940
child 224247 b87f8e6760d9693c2204a9ec97b7a80015539e4a
push id3979
push userraliiev@mozilla.com
push dateMon, 13 Oct 2014 16:35:44 +0000
treeherdermozilla-beta@30f2cc610691 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersMossop
bugs1037235
milestone34.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
Bug 1037235 - toolkit/loader doesn't check module compatibility r=Mossop
addon-sdk/moz.build
addon-sdk/source/lib/sdk/loader/cuddlefish.js
addon-sdk/source/lib/toolkit/loader.js
addon-sdk/source/modules/system/XulApp.js
addon-sdk/source/modules/system/moz.build
addon-sdk/source/test/test-cuddlefish.js
addon-sdk/source/test/test-loader.js
browser/devtools/shared/test/unit/head.js
browser/devtools/shared/test/unit/test_bezierCanvas.js
browser/devtools/shared/test/unit/test_cubicBezier.js
browser/devtools/shared/test/unit/test_undoStack.js
browser/devtools/shared/test/unit/xpcshell.ini
browser/devtools/styleinspector/test/unit/head.js
browser/devtools/styleinspector/test/unit/test_parseDeclarations.js
browser/devtools/styleinspector/test/unit/test_parseSingleValue.js
browser/devtools/styleinspector/test/unit/xpcshell.ini
toolkit/components/osfile/tests/xpcshell/test_loader.js
toolkit/components/osfile/tests/xpcshell/test_read_write.js
toolkit/components/url-classifier/tests/unit/head_urlclassifier.js
toolkit/devtools/acorn/tests/unit/head_acorn.js
toolkit/devtools/apps/tests/unit/head_apps.js
toolkit/devtools/discovery/tests/unit/head.js
toolkit/devtools/discovery/tests/unit/test_discovery.js
toolkit/devtools/discovery/tests/unit/xpcshell.ini
toolkit/devtools/jsbeautify/tests/unit/head_jsbeautify.js
toolkit/devtools/pretty-fast/tests/unit/head_pretty-fast.js
toolkit/devtools/qrcode/tests/unit/head.js
toolkit/devtools/qrcode/tests/unit/test_encode.js
toolkit/devtools/qrcode/tests/unit/xpcshell.ini
toolkit/devtools/server/actors/webapps.js
toolkit/devtools/server/tests/unit/head_dbg.js
toolkit/devtools/tern/tests/unit/head_tern.js
toolkit/devtools/tests/unit/head_devtools.js
toolkit/devtools/transport/tests/unit/head_dbg.js
toolkit/devtools/webconsole/test/unit/head.js
toolkit/devtools/webconsole/test/unit/test_js_property_provider.js
toolkit/devtools/webconsole/test/unit/test_network_helper.js
toolkit/devtools/webconsole/test/unit/xpcshell.ini
--- a/addon-sdk/moz.build
+++ b/addon-sdk/moz.build
@@ -1,13 +1,16 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
 BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
 
-DIRS += ["source/modules/system"]
-
 EXTRA_JS_MODULES.sdk += [
     'source/app-extension/bootstrap.js',
 ]
+
+EXTRA_JS_MODULES.sdk.system += [
+    'source/modules/system/Startup.js',
+    'source/modules/system/XulApp.js',
+]
--- a/addon-sdk/source/lib/sdk/loader/cuddlefish.js
+++ b/addon-sdk/source/lib/sdk/loader/cuddlefish.js
@@ -9,94 +9,44 @@ module.metadata = {
 
 // This module is manually loaded by bootstrap.js in a sandbox and immediatly
 // put in module cache so that it is never loaded in any other way.
 
 /* Workarounds to include dependencies in the manifest
 require('chrome')                  // Otherwise CFX will complain about Components
 require('toolkit/loader')          // Otherwise CFX will stip out loader.js
 require('sdk/addon/runner')        // Otherwise CFX will stip out addon/runner.js
-require('sdk/system/xul-app')      // Otherwise CFX will stip out sdk/system/xul-app
 */
 
 const { classes: Cc, Constructor: CC, interfaces: Ci, utils: Cu } = Components;
 
+const {
+  incompatibility
+} = Cu.import("resource://gre/modules/sdk/system/XulApp.js", {}).XulApp;
+
 // `loadSandbox` is exposed by bootstrap.js
 const loaderURI = module.uri.replace("sdk/loader/cuddlefish.js",
                                      "toolkit/loader.js");
-const xulappURI = module.uri.replace("loader/cuddlefish.js",
-                                     "system/xul-app.js");
 // We need to keep a reference to the sandbox in order to unload it in
 // bootstrap.js
 
 const loaderSandbox = loadSandbox(loaderURI);
 const loaderModule = loaderSandbox.exports;
 
-const xulappSandbox = loadSandbox(xulappURI);
-const xulappModule = xulappSandbox.exports;
-
 const { override, load } = loaderModule;
 
-/**
- * Ensure the current application satisfied the requirements specified in the
- * module given. If not, an exception related to the incompatibility is
- * returned; `null` otherwise.
- *
- * @param {Object} module
- *  The module to check
- * @returns {Error}
- */
-function incompatibility(module) {
-  let { metadata, id } = module;
-
-  // if metadata or engines are not specified we assume compatibility is not
-  // an issue.
-  if (!metadata || !("engines" in metadata))
-    return null;
-
-  let { engines } = metadata;
-
-  if (engines === null || typeof(engines) !== "object")
-    return new Error("Malformed engines' property in metadata");
-
-  let applications = Object.keys(engines);
-
-  let versionRange;
-  applications.forEach(function(name) {
-    if (xulappModule.is(name)) {
-      versionRange = engines[name];
-      // Continue iteration. We want to ensure the module doesn't
-      // contain a typo in the applications' name or some unknown
-      // application - `is` function throws an exception in that case.
-    }
-  });
-
-  if (typeof(versionRange) === "string") {
-    if (xulappModule.satisfiesVersion(versionRange))
-      return null;
-
-    return new Error("Unsupported Application version: The module " + id +
-            " currently supports only version " + versionRange + " of " +
-            xulappModule.name + ".");
-  }
-
-  return new Error("Unsupported Application: The module " + id +
-            " currently supports only " + applications.join(", ") + ".")
-}
-
 function CuddlefishLoader(options) {
   let { manifest } = options;
 
   options = override(options, {
     // Put `api-utils/loader` and `api-utils/cuddlefish` loaded as JSM to module
     // cache to avoid subsequent loads via `require`.
     modules: override({
       'toolkit/loader': loaderModule,
-      'sdk/loader/cuddlefish': exports,
-      'sdk/system/xul-app': xulappModule
+      'sdk/loader/cuddlefish': exports
     }, options.modules),
     resolve: function resolve(id, requirer) {
       let entry = requirer && requirer in manifest && manifest[requirer];
       let uri = null;
 
       // If manifest entry for this requirement is present we follow manifest.
       // Note: Standard library modules like 'panel' will be present in
       // manifest unless they were moved to platform.
--- a/addon-sdk/source/lib/toolkit/loader.js
+++ b/addon-sdk/source/lib/toolkit/loader.js
@@ -37,16 +37,19 @@ const systemPrincipal = CC('@mozilla.org
 const { loadSubScript } = Cc['@mozilla.org/moz/jssubscript-loader;1'].
                      getService(Ci.mozIJSSubScriptLoader);
 const { notifyObservers } = Cc['@mozilla.org/observer-service;1'].
                         getService(Ci.nsIObserverService);
 const { NetUtil } = Cu.import("resource://gre/modules/NetUtil.jsm", {});
 const { Reflect } = Cu.import("resource://gre/modules/reflect.jsm", {});
 const { ConsoleAPI } = Cu.import("resource://gre/modules/devtools/Console.jsm");
 const { join: pathJoin, normalize, dirname } = Cu.import("resource://gre/modules/osfile/ospath_unix.jsm");
+const {
+  incompatibility
+} = Cu.import("resource://gre/modules/sdk/system/XulApp.js", {}).XulApp;
 
 // Define some shortcuts.
 const bind = Function.call.bind(Function.bind);
 const getOwnPropertyNames = Object.getOwnPropertyNames;
 const getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
 const define = Object.defineProperties;
 const prototypeOf = Object.getPrototypeOf;
 const create = Object.create;
@@ -344,16 +347,22 @@ const load = iced(function load(loader, 
       message: { value: message, writable: true, configurable: true },
       fileName: { value: fileName, writable: true, configurable: true },
       lineNumber: { value: lineNumber, writable: true, configurable: true },
       stack: { value: serializeStack(frames), writable: true, configurable: true },
       toString: { value: function() toString, writable: true, configurable: true },
     });
   }
 
+  let (error = incompatibility(module)) {
+    if (error) {
+      throw error;
+    }
+  }
+
   if (module.exports && typeof(module.exports) === 'object')
     freeze(module.exports);
 
   return module;
 });
 exports.load = load;
 
 // Utility function to normalize module `uri`s so they have `.js` extension.
--- a/addon-sdk/source/modules/system/XulApp.js
+++ b/addon-sdk/source/modules/system/XulApp.js
@@ -178,8 +178,56 @@ function satisfiesVersion(version, versi
     let [, lowMod, lowVer, highMod, highVer] = matches;
 
     return compareVersion(version, lowMod, lowVer) && (highVer !== undefined
       ? compareVersion(version, highMod, highVer)
       : true);
   });
 }
 exports.satisfiesVersion = satisfiesVersion;
+
+/**
+ * Ensure the current application satisfied the requirements specified in the
+ * module given. If not, an exception related to the incompatibility is
+ * returned; `null` otherwise.
+ *
+ * @param {Object} module
+ *  The module to check
+ * @returns {Error}
+ */
+function incompatibility(module) {
+  let { metadata, id } = module;
+
+  // if metadata or engines are not specified we assume compatibility is not
+  // an issue.
+  if (!metadata || !("engines" in metadata))
+    return null;
+
+  let { engines } = metadata;
+
+  if (engines === null || typeof(engines) !== "object")
+    return new Error("Malformed engines' property in metadata");
+
+  let applications = Object.keys(engines);
+
+  let versionRange;
+  applications.forEach(function(name) {
+    if (is(name)) {
+      versionRange = engines[name];
+      // Continue iteration. We want to ensure the module doesn't
+      // contain a typo in the applications' name or some unknown
+      // application - `is` function throws an exception in that case.
+    }
+  });
+
+  if (typeof(versionRange) === "string") {
+    if (satisfiesVersion(versionRange))
+      return null;
+
+    return new Error("Unsupported Application version: The module " + id +
+            " currently supports only version " + versionRange + " of " +
+            name + ".");
+  }
+
+  return new Error("Unsupported Application: The module " + id +
+            " currently supports only " + applications.join(", ") + ".")
+}
+exports.incompatibility = incompatibility;
deleted file mode 100644
--- a/addon-sdk/source/modules/system/moz.build
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# 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/.
-
-EXTRA_JS_MODULES.sdk.system += [
-    'Startup.js',
-    'XulApp.js',
-]
--- a/addon-sdk/source/test/test-cuddlefish.js
+++ b/addon-sdk/source/test/test-cuddlefish.js
@@ -1,15 +1,15 @@
 /* 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';
 
 const { Loader, Require, unload, override } = require('sdk/loader/cuddlefish');
+const app = require('sdk/system/xul-app');
 const packaging = require('@loader/options');
 
 exports['test loader'] = function(assert) {
   var prints = [];
   function print(message) {
     prints.push(message);
   }
 
@@ -39,9 +39,24 @@ exports['test loader'] = function(assert
   });
 
   unload(loader, 'test');
 
   assert.equal(unloadsCalled, 'ba',
                'loader.unload() must call listeners in LIFO order.');
 };
 
-require('test').run(exports);
+exports['test loader on unsupported modules'] = function(assert) {
+  let loader = Loader({});
+  let err = "";
+  assert.throws(() => {
+    if (!app.is('Firefox')) {
+      require('./fixtures/loader/unsupported/firefox');
+    }
+    else {
+      require('./fixtures/loader/unsupported/fennec');
+    }
+  }, /^Unsupported Application/, "throws Unsupported Application");
+
+  unload(loader);
+};
+
+require('sdk/test').run(exports);
--- a/addon-sdk/source/test/test-loader.js
+++ b/addon-sdk/source/test/test-loader.js
@@ -1,24 +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/. */
-
 'use strict';
 
 let {
   Loader, main, unload, parseStack, generateMap, resolve, join
 } = require('toolkit/loader');
 let { readURI } = require('sdk/net/url');
 
 let root = module.uri.substr(0, module.uri.lastIndexOf('/'))
 
-
 // The following adds Debugger constructor to the global namespace.
 const { Cu } = require('chrome');
+const app = require('sdk/system/xul-app');
+
 const { addDebuggerToGlobal } = Cu.import('resource://gre/modules/jsdebugger.jsm', {});
 addDebuggerToGlobal(this);
 
 exports['test resolve'] = function (assert) {
   let cuddlefish_id = 'sdk/loader/cuddlefish';
   assert.equal(resolve('../index.js', './dir/c.js'), './index.js');
   assert.equal(resolve('./index.js', './dir/c.js'), './dir/index.js');
   assert.equal(resolve('./dir/c.js', './index.js'), './dir/c.js');
@@ -326,17 +326,17 @@ exports['test invisibleToDebugger: true'
     assert.ok(true, 'debugger did not add invisible value');
   }
 };
 
 exports['test console global by default'] = function (assert) {
   let uri = root + '/fixtures/loader/globals/';
   let loader = Loader({ paths: { '': uri }});
   let program = main(loader, 'main');
- 
+
   assert.ok(typeof program.console === 'object', 'global `console` exists');
   assert.ok(typeof program.console.log === 'function', 'global `console.log` exists');
 
   let loader2 = Loader({ paths: { '': uri }, globals: { console: fakeConsole }});
   let program2 = main(loader2, 'main');
 
   assert.equal(program2.console, fakeConsole,
     'global console can be overridden with Loader options');
@@ -369,9 +369,24 @@ exports['test shared globals'] = functio
 exports["test require#resolve"] = function(assert) {
   let root = require.resolve("sdk/tabs").replace(/commonjs\.path\/(.*)$/, "") + "commonjs.path/";
   assert.ok(/^resource:\/\/extensions\.modules\.[a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{12}-at-jetpack\.commonjs\.path\/$/.test(root), "correct resolution root");
 
   assert.equal(root + "sdk/tabs.js", require.resolve("sdk/tabs"), "correct resolution of sdk module");
   assert.equal(root + "toolkit/loader.js", require.resolve("toolkit/loader"), "correct resolution of sdk module");
 };
 
-require('test').run(exports);
+exports['test loader on unsupported modules'] = function(assert) {
+  let loader = Loader({});
+  let err = "";
+  assert.throws(() => {
+    if (!app.is('Firefox')) {
+      require('./fixtures/loader/unsupported/firefox');
+    }
+    else {
+      require('./fixtures/loader/unsupported/fennec');
+    }
+  }, /^Unsupported Application/, "throws Unsupported Application");
+
+  unload(loader);
+};
+
+require('sdk/test').run(exports);
new file mode 100644
--- /dev/null
+++ b/browser/devtools/shared/test/unit/head.js
@@ -0,0 +1,16 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+const Cr = Components.results;
+const CC = Components.Constructor;
+
+// We also need a valid nsIXulAppInfo service as Webapps.jsm is querying it
+Cu.import("resource://testing-common/AppInfo.jsm");
+updateAppInfo();
+
+let {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
+let require = devtools.require;
--- a/browser/devtools/shared/test/unit/test_bezierCanvas.js
+++ b/browser/devtools/shared/test/unit/test_bezierCanvas.js
@@ -2,19 +2,16 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 // Tests the BezierCanvas API in the CubicBezierWidget module
 
-const Cu = Components.utils;
-let {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
-let require = devtools.require;
 let {CubicBezier, BezierCanvas} = require("devtools/shared/widgets/CubicBezierWidget");
 
 function run_test() {
   offsetsGetterReturnsData();
   convertsOffsetsToCoordinates();
   plotsCanvas();
 }
 
--- a/browser/devtools/shared/test/unit/test_cubicBezier.js
+++ b/browser/devtools/shared/test/unit/test_cubicBezier.js
@@ -2,19 +2,16 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 // Tests the CubicBezier API in the CubicBezierWidget module
 
-const Cu = Components.utils;
-let {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
-let require = devtools.require;
 let {CubicBezier} = require("devtools/shared/widgets/CubicBezierWidget");
 
 function run_test() {
   throwsWhenMissingCoordinates();
   throwsWhenIncorrectCoordinates();
   convertsStringCoordinates();
   coordinatesToStringOutputsAString();
   pointGettersReturnPointCoordinatesArrays();
--- a/browser/devtools/shared/test/unit/test_undoStack.js
+++ b/browser/devtools/shared/test/unit/test_undoStack.js
@@ -1,14 +1,13 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
-const Cu = Components.utils;
 let {Loader} = Cu.import("resource://gre/modules/commonjs/toolkit/loader.js", {});
 
 let loader = new Loader.Loader({
   paths: {
     "": "resource://gre/modules/commonjs/",
     "devtools": "resource:///modules/devtools",
   },
   globals: {},
--- a/browser/devtools/shared/test/unit/xpcshell.ini
+++ b/browser/devtools/shared/test/unit/xpcshell.ini
@@ -1,8 +1,8 @@
 [DEFAULT]
-head =
+head = head.js
 tail =
 firefox-appdir = browser
 
 [test_bezierCanvas.js]
 [test_cubicBezier.js]
 [test_undoStack.js]
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleinspector/test/unit/head.js
@@ -0,0 +1,15 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+const Cr = Components.results;
+const CC = Components.Constructor;
+
+// We also need a valid nsIXulAppInfo
+Cu.import("resource://testing-common/AppInfo.jsm");
+updateAppInfo();
+
+Cu.import("resource://gre/modules/devtools/Loader.jsm");
--- a/browser/devtools/styleinspector/test/unit/test_parseDeclarations.js
+++ b/browser/devtools/styleinspector/test/unit/test_parseDeclarations.js
@@ -1,15 +1,13 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
-const Cu = Components.utils;
-Cu.import("resource://gre/modules/devtools/Loader.jsm");
 const {parseDeclarations} = devtools.require("devtools/styleinspector/css-parsing-utils");
 
 const TEST_DATA = [
   // Simple test
   {
     input: "p:v;",
     expected: [{name: "p", value: "v", priority: ""}]
   },
--- a/browser/devtools/styleinspector/test/unit/test_parseSingleValue.js
+++ b/browser/devtools/styleinspector/test/unit/test_parseSingleValue.js
@@ -1,15 +1,13 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
-const Cu = Components.utils;
-Cu.import("resource://gre/modules/devtools/Loader.jsm");
 const {parseSingleValue} = devtools.require("devtools/styleinspector/css-parsing-utils");
 
 const TEST_DATA = [
   {input: null, throws: true},
   {input: undefined, throws: true},
   {input: "", expected: {value: "", priority: ""}},
   {input: "  \t \t \n\n  ", expected: {value: "", priority: ""}},
   {input: "blue", expected: {value: "blue", priority: ""}},
--- a/browser/devtools/styleinspector/test/unit/xpcshell.ini
+++ b/browser/devtools/styleinspector/test/unit/xpcshell.ini
@@ -1,7 +1,7 @@
 [DEFAULT]
-head =
+head = head.js
 tail =
 firefox-appdir = browser
 
 [test_parseDeclarations.js]
 [test_parseSingleValue.js]
--- a/toolkit/components/osfile/tests/xpcshell/test_loader.js
+++ b/toolkit/components/osfile/tests/xpcshell/test_loader.js
@@ -2,23 +2,26 @@
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 /**
  * Test that OS.File can be loaded using the CommonJS loader.
  */
 
+// We also need a valid nsIXulAppInfo
+Cu.import("resource://testing-common/AppInfo.jsm");
+updateAppInfo();
+
 let { Loader } = Components.utils.import('resource://gre/modules/commonjs/toolkit/loader.js', {});
 
 function run_test() {
   run_next_test();
 }
 
-
 add_task(function*() {
   let dataDir = Services.io.newFileURI(do_get_file("test_loader/", true)).spec + "/";
   let loader = Loader.Loader({
     paths: {'': dataDir }
   });
 
   let require = Loader.Require(loader, Loader.Module('module_test_loader', 'foo'));
   do_print("Require is ready");
@@ -29,9 +32,8 @@ add_task(function*() {
          (error.message ? error.message : String(error)) + '\n' +
          (error.stack || error.fileName + ': ' + error.lineNumber) + '\n');
 
     throw error;
   }
 
   do_print("Require has worked");
 });
-
--- a/toolkit/components/osfile/tests/xpcshell/test_read_write.js
+++ b/toolkit/components/osfile/tests/xpcshell/test_read_write.js
@@ -1,15 +1,13 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
-let {utils: Cu} = Components;
-
 let SHARED_PATH;
 
 let EXISTING_FILE = do_get_file("xpcshell.ini").path;
 
 add_task(function* init() {
   do_get_profile();
   SHARED_PATH = OS.Path.join(OS.Constants.Path.profileDir, "test_osfile_read.tmp");
 });
--- a/toolkit/components/url-classifier/tests/unit/head_urlclassifier.js
+++ b/toolkit/components/url-classifier/tests/unit/head_urlclassifier.js
@@ -6,16 +6,20 @@ function dumpn(s) {
 const NS_APP_USER_PROFILE_50_DIR = "ProfD";
 const NS_APP_USER_PROFILE_LOCAL_50_DIR = "ProfLD";
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 const Cr = Components.results;
 
+// We also need a valid nsIXulAppInfo
+Cu.import("resource://testing-common/AppInfo.jsm");
+updateAppInfo();
+
 Cu.import("resource://testing-common/httpd.js");
 
 do_get_profile();
 
 var dirSvc = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties);
 
 var iosvc = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
 
--- a/toolkit/devtools/acorn/tests/unit/head_acorn.js
+++ b/toolkit/devtools/acorn/tests/unit/head_acorn.js
@@ -1,10 +1,15 @@
 "use strict";
 const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
+
+// We also need a valid nsIXulAppInfo
+Cu.import("resource://testing-common/AppInfo.jsm");
+updateAppInfo();
+
 const { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
 const { require } = devtools;
 
 
 function isObject(value) {
   return typeof value === "object" && value !== null;
 }
 
@@ -63,9 +68,9 @@ let listener = {
       }
     }
 
     do_throw("head_acorn.js got console message: " + string + "\n");
   }
 };
 
 let consoleService = Cc["@mozilla.org/consoleservice;1"].getService(Ci.nsIConsoleService);
-consoleService.registerListener(listener);
\ No newline at end of file
+consoleService.registerListener(listener);
--- a/toolkit/devtools/apps/tests/unit/head_apps.js
+++ b/toolkit/devtools/apps/tests/unit/head_apps.js
@@ -2,22 +2,35 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 const Cr = Components.results;
 const CC = Components.Constructor;
 
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/FileUtils.jsm");
+
+// We have to setup a profile, otherwise indexed db used by webapps
+// will throw random exception when trying to get profile folder
+do_get_profile();
+
+// The webapps dir isn't registered on b2g xpcshell tests,
+// we have to manually set it to the directory service.
+do_get_webappsdir();
+
+// We also need a valid nsIXulAppInfo
+Cu.import("resource://testing-common/AppInfo.jsm");
+updateAppInfo();
+
 Cu.import("resource://gre/modules/devtools/dbg-server.jsm");
 Cu.import("resource://gre/modules/devtools/dbg-client.jsm");
 
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/FileUtils.jsm");
 
 let gClient, gActor;
 
 function connect(onDone) {
   // Initialize a loopback remote protocol connection
   DebuggerServer.init(function () { return true; });
   // We need to register browser actors to have `listTabs` working
   // and also have a root actor
@@ -61,31 +74,19 @@ function installTestApp(zipName, appId, 
     }
     do_check_false("error" in aResponse);
 
     onDone();
   });
 };
 
 function setup() {
-  // We have to setup a profile, otherwise indexed db used by webapps
-  // will throw random exception when trying to get profile folder
-  do_get_profile();
-
-  // The webapps dir isn't registered on b2g xpcshell tests,
-  // we have to manually set it to the directory service.
-  do_get_webappsdir();
-
-  // We also need a valid nsIXulAppInfo service as Webapps.jsm is querying it
-  Components.utils.import("resource://testing-common/AppInfo.jsm");
-  updateAppInfo();
-
   // We have to toggle this flag in order to have apps being listed in getAll
   // as only launchable apps are returned
-  Components.utils.import('resource://gre/modules/Webapps.jsm');
+  Cu.import('resource://gre/modules/Webapps.jsm');
   DOMApplicationRegistry.allAppsLaunchable = true;
 
   // Mock WebappOSUtils
   Cu.import("resource://gre/modules/WebappOSUtils.jsm");
   WebappOSUtils.getPackagePath = function(aApp) {
     return aApp.basePath + "/" + aApp.id;
   }
 }
new file mode 100644
--- /dev/null
+++ b/toolkit/devtools/discovery/tests/unit/head.js
@@ -0,0 +1,11 @@
+"use strict";
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+const Cr = Components.results;
+
+// We also need a valid nsIXulAppInfo
+Cu.import("resource://testing-common/AppInfo.jsm");
+updateAppInfo();
+
+const { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
--- a/toolkit/devtools/discovery/tests/unit/test_discovery.js
+++ b/toolkit/devtools/discovery/tests/unit/test_discovery.js
@@ -1,24 +1,20 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
-const Cu = Components.utils;
-
 const { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
 Services.prefs.setBoolPref("devtools.discovery.log", true);
 
 do_register_cleanup(() => {
   Services.prefs.clearUserPref("devtools.discovery.log");
 });
 
-const { devtools } =
-  Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
 const { Promise: promise } =
   Cu.import("resource://gre/modules/Promise.jsm", {});
 const { require } = devtools;
 const EventEmitter = require("devtools/toolkit/event-emitter");
 const discovery = require("devtools/toolkit/discovery/discovery");
 const { setTimeout, clearTimeout } = require("sdk/timers");
 
 function log(msg) {
--- a/toolkit/devtools/discovery/tests/unit/xpcshell.ini
+++ b/toolkit/devtools/discovery/tests/unit/xpcshell.ini
@@ -1,6 +1,6 @@
 [DEFAULT]
-head =
+head = head.js
 tail =
 
 [test_discovery.js]
 skip-if = toolkit == 'gonk' && debug # Debug doesn't like settings read
--- a/toolkit/devtools/jsbeautify/tests/unit/head_jsbeautify.js
+++ b/toolkit/devtools/jsbeautify/tests/unit/head_jsbeautify.js
@@ -4,15 +4,20 @@
 
 "use strict";
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 const Cr = Components.results;
 
+// We also need a valid nsIXulAppInfo
+Cu.import("resource://testing-common/AppInfo.jsm");
+updateAppInfo();
+
 const { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
+
 const { require } = devtools;
 
 var beautify = require("devtools/jsbeautify");
 var SanityTest = require('devtools/toolkit/jsbeautify/sanitytest');
 var Urlencoded = require('devtools/toolkit/jsbeautify/urlencode_unpacker');
 var {run_beautifier_tests} = require('devtools/toolkit/jsbeautify/beautify-tests');
--- a/toolkit/devtools/pretty-fast/tests/unit/head_pretty-fast.js
+++ b/toolkit/devtools/pretty-fast/tests/unit/head_pretty-fast.js
@@ -1,14 +1,18 @@
 "use strict";
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 const Cr = Components.results;
 
+// We also need a valid nsIXulAppInfo
+Cu.import("resource://testing-common/AppInfo.jsm");
+updateAppInfo();
+
 const { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
 const { require } = devtools;
 
 this.sourceMap = require("source-map");
 this.acorn = require("acorn/acorn");
 this.prettyFast = require("devtools/pretty-fast");
 const { console } = Cu.import("resource://gre/modules/devtools/Console.jsm", {});
 
new file mode 100644
--- /dev/null
+++ b/toolkit/devtools/qrcode/tests/unit/head.js
@@ -0,0 +1,11 @@
+"use strict";
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+const Cr = Components.results;
+
+// We also need a valid nsIXulAppInfo
+Cu.import("resource://testing-common/AppInfo.jsm");
+updateAppInfo();
+
+const { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
--- a/toolkit/devtools/qrcode/tests/unit/test_encode.js
+++ b/toolkit/devtools/qrcode/tests/unit/test_encode.js
@@ -1,18 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 "use strict";
 
 /**
  * Test encoding a simple message.
  */
 
-const { utils: Cu } = Components;
-const { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
 const { require } = devtools;
 
 const QR = require("devtools/toolkit/qrcode/index");
 
 function run_test() {
   let imgData = QR.encodeToDataURI("HELLO", "L");
   do_check_eq(imgData.src,
               "data:image/gif;base64,R0lGODdhOgA6AIAAAAAAAP///ywAAAAAOgA6AAAC" +
--- a/toolkit/devtools/qrcode/tests/unit/xpcshell.ini
+++ b/toolkit/devtools/qrcode/tests/unit/xpcshell.ini
@@ -1,5 +1,5 @@
 [DEFAULT]
-head =
+head = head.js
 tail =
 
 [test_encode.js]
--- a/toolkit/devtools/server/actors/webapps.js
+++ b/toolkit/devtools/server/actors/webapps.js
@@ -799,17 +799,18 @@ WebappsActor.prototype = {
     if (!manifestURL) {
       return { error: "missingParameter",
                message: "missing parameter manifestURL" };
     }
 
     let deferred = promise.defer();
 
     if (Services.appinfo.ID &&
-        Services.appinfo.ID != "{3c2e2abc-06d4-11e1-ac3b-374f68613e61}") {
+        Services.appinfo.ID != "{3c2e2abc-06d4-11e1-ac3b-374f68613e61}" &&
+        Services.appinfo.ID != "xpcshell@tests.mozilla.org") {
       return { error: "notSupported",
                message: "Not B2G. Can't launch app." };
     }
 
     DOMApplicationRegistry.launch(
       aRequest.manifestURL,
       aRequest.startPoint || "",
       Date.now(),
--- a/toolkit/devtools/server/tests/unit/head_dbg.js
+++ b/toolkit/devtools/server/tests/unit/head_dbg.js
@@ -2,16 +2,20 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 const Cr = Components.results;
 
+// We also need a valid nsIXulAppInfo
+Cu.import("resource://testing-common/AppInfo.jsm");
+updateAppInfo();
+
 const { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
 const { worker } = Cu.import("resource://gre/modules/devtools/worker-loader.js", {})
 const {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
 const { Task } = Cu.import("resource://gre/modules/Task.jsm", {});
 const { promiseInvoke } = devtools.require("devtools/async-utils");
 
 const Services = devtools.require("Services");
 // Always log packets when running tests. runxpcshelltests.py will throw
--- a/toolkit/devtools/tern/tests/unit/head_tern.js
+++ b/toolkit/devtools/tern/tests/unit/head_tern.js
@@ -1,4 +1,11 @@
 "use strict";
 const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+// We also need a valid nsIXulAppInfo
+Cu.import("resource://testing-common/AppInfo.jsm");
+updateAppInfo();
+
 const { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
 const { require } = devtools;
--- a/toolkit/devtools/tests/unit/head_devtools.js
+++ b/toolkit/devtools/tests/unit/head_devtools.js
@@ -1,14 +1,18 @@
 "use strict";
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 const Cr = Components.results;
 
+// We also need a valid nsIXulAppInfo
+Cu.import("resource://testing-common/AppInfo.jsm");
+updateAppInfo();
+
 Cu.import("resource://gre/modules/devtools/Loader.jsm");
 Cu.import("resource://gre/modules/devtools/DevToolsUtils.jsm");
 
 // Register a console listener, so console messages don't just disappear
 // into the ether.
 let errorCount = 0;
 let listener = {
   observe: function (aMessage) {
@@ -36,8 +40,9 @@ let listener = {
       DebuggerServer.xpcInspector.exitNestedEventLoop();
     }
     do_throw("head_dbg.js got console message: " + string + "\n");
   }
 };
 
 let consoleService = Cc["@mozilla.org/consoleservice;1"].getService(Ci.nsIConsoleService);
 consoleService.registerListener(listener);
+
--- a/toolkit/devtools/transport/tests/unit/head_dbg.js
+++ b/toolkit/devtools/transport/tests/unit/head_dbg.js
@@ -3,16 +3,20 @@
 
 "use strict";
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 const Cr = Components.results;
 const CC = Components.Constructor;
 
+// We also need a valid nsIXulAppInfo
+Cu.import("resource://testing-common/AppInfo.jsm");
+updateAppInfo();
+
 const { devtools } =
   Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
 const { Promise: promise } =
   Cu.import("resource://gre/modules/Promise.jsm", {});
 
 const Services = devtools.require("Services");
 const DevToolsUtils = devtools.require("devtools/toolkit/DevToolsUtils.js");
 
new file mode 100644
--- /dev/null
+++ b/toolkit/devtools/webconsole/test/unit/head.js
@@ -0,0 +1,11 @@
+"use strict";
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+const Cr = Components.results;
+
+// We also need a valid nsIXulAppInfo
+Cu.import("resource://testing-common/AppInfo.jsm");
+updateAppInfo();
+
+const { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
--- a/toolkit/devtools/webconsole/test/unit/test_js_property_provider.js
+++ b/toolkit/devtools/webconsole/test/unit/test_js_property_provider.js
@@ -1,14 +1,13 @@
 /* -*- js-indent-level: 2; indent-tabs-mode: nil -*- */
 // Any copyright is dedicated to the Public Domain.
 // http://creativecommons.org/publicdomain/zero/1.0/
 
 "use strict";
-const { devtools } = Components.utils.import("resource://gre/modules/devtools/Loader.jsm", {});
 let JSPropertyProvider = devtools.require("devtools/toolkit/webconsole/utils").JSPropertyProvider;
 
 Components.utils.import("resource://gre/modules/jsdebugger.jsm");
 addDebuggerToGlobal(this);
 
 function run_test() {
   const testArray = 'var testArray = [\
     {propA: "A"},\
--- a/toolkit/devtools/webconsole/test/unit/test_network_helper.js
+++ b/toolkit/devtools/webconsole/test/unit/test_network_helper.js
@@ -1,14 +1,12 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
-const Cu = Components.utils;
-const { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
 
 Object.defineProperty(this, "NetworkHelper", {
   get: function() {
     return devtools.require("devtools/toolkit/webconsole/network-helper");
   },
   configurable: true,
   writeable: false,
   enumerable: true
--- a/toolkit/devtools/webconsole/test/unit/xpcshell.ini
+++ b/toolkit/devtools/webconsole/test/unit/xpcshell.ini
@@ -1,7 +1,7 @@
 [DEFAULT]
-head =
+head = head.js
 tail =
 support-files =
 
 [test_js_property_provider.js]
 [test_network_helper.js]