Bug 1369801 - dt-addon-xpcshell: load devtools addon for xpcshell tests draft
authorJulian Descottes <jdescottes@mozilla.com>
Tue, 11 Jul 2017 12:30:22 +0200
changeset 606793 436992aa2e440069aa7e53395bd8d576eca37a62
parent 606792 f2ac3f0f27b563fb6ce2516f1d6ba6c4359039bb
child 606794 e8dffbf17411b777ef80f201c5e112a47daf5088
push id67804
push userjdescottes@mozilla.com
push dateTue, 11 Jul 2017 12:55:28 +0000
bugs1369801
milestone56.0a1
Bug 1369801 - dt-addon-xpcshell: load devtools addon for xpcshell tests MozReview-Commit-ID: 4EyclGKca0t
devtools/bootstrap.js
devtools/client/animationinspector/test/unit/xpcshell.ini
devtools/client/inspector/grids/test/unit/xpcshell.ini
devtools/client/jar.mn
devtools/client/memory/test/unit/xpcshell.ini
devtools/client/performance/test/unit/xpcshell.ini
devtools/client/preferences/DevToolsPreferences.jsm
devtools/client/responsive.html/test/unit/xpcshell.ini
devtools/client/shared/redux/middleware/test/xpcshell.ini
devtools/client/shared/test/unit/xpcshell.ini
devtools/client/shared/vendor/stringvalidator/tests/unit/xpcshell.ini
devtools/client/sourceeditor/tern/tests/unit/xpcshell.ini
devtools/client/webconsole/net/test/unit/xpcshell.ini
devtools/platform/tests/unit/xpcshell.ini
devtools/server/tests/unit/xpcshell.ini
devtools/shared/acorn/tests/unit/xpcshell.ini
devtools/shared/discovery/tests/unit/xpcshell.ini
devtools/shared/heapsnapshot/tests/unit/xpcshell.ini
devtools/shared/jsbeautify/tests/unit/xpcshell.ini
devtools/shared/moz.build
devtools/shared/performance/test/xpcshell.ini
devtools/shared/platform/content/test/xpcshell.ini
devtools/shared/pretty-fast/tests/unit/xpcshell.ini
devtools/shared/qrcode/tests/unit/xpcshell.ini
devtools/shared/security/tests/unit/xpcshell.ini
devtools/shared/tests/shared-xpcshell-head.js
devtools/shared/tests/unit/xpcshell.ini
devtools/shared/transport/tests/unit/xpcshell.ini
devtools/shared/webconsole/test/unit/xpcshell.ini
--- a/devtools/bootstrap.js
+++ b/devtools/bootstrap.js
@@ -11,135 +11,37 @@ const Cc = Components.classes;
 const Cu = Components.utils;
 const Ci = Components.interfaces;
 const Cm = Components.manager;
 
 Cm.QueryInterface(Ci.nsIComponentRegistrar);
 
 const {XPCOMUtils} = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {});
 const {Services} = Cu.import("resource://gre/modules/Services.jsm", {});
-const {NetUtil} = Cu.import("resource://gre/modules/NetUtil.jsm", {});
-const {AppConstants} = Cu.import("resource://gre/modules/AppConstants.jsm", {});
 
 XPCOMUtils.defineLazyGetter(this, "DevToolsStartup", function () {
   return Cu.import("resource://devtools/client/devtools-startup.js", {}).DevToolsStartup;
 });
+XPCOMUtils.defineLazyGetter(this, "DevToolsPreferences", function () {
+  return Cu.import("chrome://devtools/content/preferences/DevToolsPreferences.jsm", {}).DevToolsPreferences;
+});
 XPCOMUtils.defineLazyGetter(this, "catman", function () {
   return Cc["@mozilla.org/categorymanager;1"].getService(Ci.nsICategoryManager);
 });
 
 // MultiWindowKeyListener instance for Ctrl+Alt+R key
 let listener;
 
 function actionOccurred(id) {
   let {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
   let Telemetry = require("devtools/client/shared/telemetry");
   let telemetry = new Telemetry();
   telemetry.actionOccurred(id);
 }
 
-// Synchronously fetch the content of a given URL
-function readURI(uri) {
-  let stream = NetUtil.newChannel({
-    uri: NetUtil.newURI(uri, "UTF-8"),
-    loadUsingSystemPrincipal: true}
-  ).open2();
-  let count = stream.available();
-  let data = NetUtil.readInputStreamToString(stream, count, {
-    charset: "UTF-8"
-  });
-
-  stream.close();
-
-  return data;
-}
-
-/**
- * Interpret the processing instructions contained in a preferences file, based on a
- * limited set of supported #if statements. After we ship as an addon, we don't want to
- * introduce anymore processing instructions, so all unrecognized preprocessing
- * instructions will be treated as an error.
- *
- * This function is mostly copied from devtools/client/inspector/webpack/prefs-loader.js
- *
- * @param  {String} content
- *         The string content of a preferences file.
- * @return {String} the content stripped of preprocessing instructions.
- */
-function interpretPreprocessingInstructions(content) {
-  const ifMap = {
-    "#if MOZ_UPDATE_CHANNEL == beta": AppConstants.MOZ_UPDATE_CHANNEL === "beta",
-    "#if defined(NIGHTLY_BUILD)": AppConstants.NIGHTLY_BUILD,
-    "#ifdef MOZ_DEV_EDITION": AppConstants.MOZ_DEV_EDITION,
-    "#ifdef RELEASE_OR_BETA": AppConstants.RELEASE_OR_BETA,
-  };
-
-  let lines = content.split("\n");
-  let ignoring = false;
-  let newLines = [];
-  let continuation = false;
-  for (let line of lines) {
-    if (line.startsWith("#if")) {
-      if (!(line in ifMap)) {
-        throw new Error("missing line in ifMap: " + line);
-      }
-      ignoring = !ifMap[line];
-    } else if (line.startsWith("#else")) {
-      ignoring = !ignoring;
-    } else if (line.startsWith("#endif")) {
-      ignoring = false;
-    }
-
-    let isPrefLine = /^ *(sticky_)?pref\("([^"]+)"/.test(line);
-    if (continuation || (!ignoring && isPrefLine)) {
-      newLines.push(line);
-
-      // The call to pref(...); might span more than one line.
-      continuation = !/\);/.test(line);
-    }
-  }
-  return newLines.join("\n");
-}
-
-// Read a preference file and set all of its defined pref as default values
-// (This replicates the behavior of preferences files from mozilla-central)
-function processPrefFile(url) {
-  let content = readURI(url);
-  content = interpretPreprocessingInstructions(content);
-  content.match(/pref\("[^"]+",\s*.+\s*\)/g).forEach(item => {
-    let m = item.match(/pref\("([^"]+)",\s*(.+)\s*\)/);
-    let name = m[1];
-    let val = m[2].trim();
-
-    // Prevent overriding prefs that have been changed by the user
-    if (Services.prefs.prefHasUserValue(name)) {
-      return;
-    }
-    let defaultBranch = Services.prefs.getDefaultBranch("");
-    if ((val.startsWith("\"") && val.endsWith("\"")) ||
-        (val.startsWith("'") && val.endsWith("'"))) {
-      val = val.substr(1, val.length - 2);
-      val = val.replace(/\\"/g, '"');
-      defaultBranch.setCharPref(name, val);
-    } else if (val.match(/[0-9]+/)) {
-      defaultBranch.setIntPref(name, parseInt(val, 10));
-    } else if (val == "true" || val == "false") {
-      defaultBranch.setBoolPref(name, val == "true");
-    } else {
-      console.log("Unable to match preference type for value:", val);
-    }
-  });
-}
-
-function setPrefs() {
-  processPrefFile("chrome://devtools/content/preferences/devtools.js");
-  processPrefFile("chrome://devtools/content/preferences/debugger.js");
-  processPrefFile("chrome://devtools/content/webide/webide-prefs.js");
-}
-
 // Helper to listen to a key on all windows
 function MultiWindowKeyListener({ keyCode, ctrlKey, altKey, callback }) {
   let keyListener = function (event) {
     if (event.ctrlKey == !!ctrlKey &&
         event.altKey == !!altKey &&
         event.keyCode === keyCode) {
       callback(event);
 
@@ -256,17 +158,17 @@ function reload(event) {
   dump("Reload DevTools.  (reload-toolbox:" + reloadToolbox + ")\n");
 
   // Invalidate xul cache in order to see changes made to chrome:// files
   Services.obs.notifyObservers(null, "startupcache-invalidate");
 
   unload("reload");
 
   // Update the preferences before starting new code
-  setPrefs();
+  DevToolsPreferences.loadPrefs();
 
   const {devtools} = Cu.import("resource://devtools/shared/Loader.jsm", {});
   devtools.require("devtools/client/framework/devtools-browser");
 
   // Go over all top level windows to reload all devtools related things
   let windowsEnum = Services.wm.getEnumerator(null);
   while (windowsEnum.hasMoreElements()) {
     let window = windowsEnum.getNext();
--- a/devtools/client/animationinspector/test/unit/xpcshell.ini
+++ b/devtools/client/animationinspector/test/unit/xpcshell.ini
@@ -1,11 +1,11 @@
 [DEFAULT]
 tags = devtools
-head =
+head = ../../../../shared/tests/shared-xpcshell-head.js
 firefox-appdir = browser
 skip-if = toolkit == 'android'
 
 [test_findOptimalTimeInterval.js]
 [test_formatStopwatchTime.js]
 [test_getCssPropertyName.js]
 [test_timeScale.js]
 [test_timeScale_dimensions.js]
--- a/devtools/client/inspector/grids/test/unit/xpcshell.ini
+++ b/devtools/client/inspector/grids/test/unit/xpcshell.ini
@@ -1,6 +1,6 @@
 [DEFAULT]
 tags = devtools
 firefox-appdir = browser
-head = head.js
+head = ../../../../../shared/tests/shared-xpcshell-head.js head.js
 
 [test_compare_fragments_geometry.js]
--- a/devtools/client/jar.mn
+++ b/devtools/client/jar.mn
@@ -1,16 +1,17 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 [features/devtools@mozilla.org] chrome.jar:
 %   content devtools %content/
 *   content/preferences/debugger.js (preferences/debugger.js)
 *   content/preferences/devtools.js (preferences/devtools.js)
+    content/preferences/DevToolsPreferences.jsm (preferences/DevToolsPreferences.jsm)
     content/webide/webide-prefs.js (webide/webide-prefs.js)
     content/shared/vendor/d3.js (shared/vendor/d3.js)
     content/shared/vendor/dagre-d3.js (shared/vendor/dagre-d3.js)
     content/shared/widgets/widgets.css (shared/widgets/widgets.css)
     content/netmonitor/src/assets/styles/netmonitor.css (netmonitor/src/assets/styles/netmonitor.css)
     content/shared/widgets/VariablesView.xul (shared/widgets/VariablesView.xul)
     content/netmonitor/index.html (netmonitor/index.html)
     content/webconsole/webconsole.xul (webconsole/webconsole.xul)
--- a/devtools/client/memory/test/unit/xpcshell.ini
+++ b/devtools/client/memory/test/unit/xpcshell.ini
@@ -1,11 +1,11 @@
 [DEFAULT]
 tags = devtools devtools-memory
-head = head.js ../../../framework/test/shared-redux-head.js
+head = ../../../../shared/tests/shared-xpcshell-head.js  head.js  ../../../framework/test/shared-redux-head.js
 firefox-appdir = browser
 skip-if = toolkit == 'android'
 
 [test_action_diffing_01.js]
 [test_action_diffing_02.js]
 [test_action_diffing_03.js]
 [test_action_diffing_04.js]
 [test_action_diffing_05.js]
--- a/devtools/client/performance/test/unit/xpcshell.ini
+++ b/devtools/client/performance/test/unit/xpcshell.ini
@@ -1,11 +1,11 @@
 [DEFAULT]
 tags = devtools
-head = head.js
+head = ../../../../shared/tests/shared-xpcshell-head.js  head.js
 firefox-appdir = browser
 skip-if = toolkit == 'android'
 
 [test_frame-utils-01.js]
 [test_frame-utils-02.js]
 [test_marker-blueprint.js]
 [test_marker-utils.js]
 [test_profiler-categories.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/preferences/DevToolsPreferences.jsm
@@ -0,0 +1,114 @@
+/* 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 Cu = Components.utils;
+
+const {NetUtil} = Cu.import("resource://gre/modules/NetUtil.jsm", {});
+const {AppConstants} = Cu.import("resource://gre/modules/AppConstants.jsm", {});
+const {Services} = Cu.import("resource://gre/modules/Services.jsm", {});
+
+this.EXPORTED_SYMBOLS = ["DevToolsPreferences"];
+
+// Synchronously fetch the content of a given URL
+function readURI(uri) {
+  let stream = NetUtil.newChannel({
+    uri: NetUtil.newURI(uri, "UTF-8"),
+    loadUsingSystemPrincipal: true}
+  ).open2();
+  let count = stream.available();
+  let data = NetUtil.readInputStreamToString(stream, count, {
+    charset: "UTF-8"
+  });
+
+  stream.close();
+
+  return data;
+}
+
+/**
+ * Interpret the processing instructions contained in a preferences file, based on a
+ * limited set of supported #if statements. After we ship as an addon, we don't want to
+ * introduce anymore processing instructions, so all unrecognized preprocessing
+ * instructions will be treated as an error.
+ *
+ * This function is mostly copied from devtools/client/inspector/webpack/prefs-loader.js
+ *
+ * @param  {String} content
+ *         The string content of a preferences file.
+ * @return {String} the content stripped of preprocessing instructions.
+ */
+function interpretPreprocessingInstructions(content) {
+  const ifMap = {
+    "#if MOZ_UPDATE_CHANNEL == beta": AppConstants.MOZ_UPDATE_CHANNEL === "beta",
+    "#if defined(NIGHTLY_BUILD)": AppConstants.NIGHTLY_BUILD,
+    "#ifdef MOZ_DEV_EDITION": AppConstants.MOZ_DEV_EDITION,
+    "#ifdef RELEASE_OR_BETA": AppConstants.RELEASE_OR_BETA,
+  };
+
+  let lines = content.split("\n");
+  let ignoring = false;
+  let newLines = [];
+  let continuation = false;
+  for (let line of lines) {
+    if (line.startsWith("#if")) {
+      if (!(line in ifMap)) {
+        throw new Error("missing line in ifMap: " + line);
+      }
+      ignoring = !ifMap[line];
+    } else if (line.startsWith("#else")) {
+      ignoring = !ignoring;
+    } else if (line.startsWith("#endif")) {
+      ignoring = false;
+    }
+
+    let isPrefLine = /^ *(sticky_)?pref\("([^"]+)"/.test(line);
+    if (continuation || (!ignoring && isPrefLine)) {
+      newLines.push(line);
+
+      // The call to pref(...); might span more than one line.
+      continuation = !/\);/.test(line);
+    }
+  }
+  return newLines.join("\n");
+}
+
+// Read a preference file and set all of its defined pref as default values
+// (This replicates the behavior of preferences files from mozilla-central)
+function processPrefFile(url) {
+  let content = readURI(url);
+  content = interpretPreprocessingInstructions(content);
+  content.match(/pref\("[^"]+",\s*.+\s*\)/g).forEach(item => {
+    let m = item.match(/pref\("([^"]+)",\s*(.+)\s*\)/);
+    let name = m[1];
+    let val = m[2].trim();
+
+    // Prevent overriding prefs that have been changed by the user
+    if (Services.prefs.prefHasUserValue(name)) {
+      return;
+    }
+    let defaultBranch = Services.prefs.getDefaultBranch("");
+    if ((val.startsWith("\"") && val.endsWith("\"")) ||
+        (val.startsWith("'") && val.endsWith("'"))) {
+      val = val.substr(1, val.length - 2);
+      val = val.replace(/\\"/g, '"');
+      defaultBranch.setCharPref(name, val);
+    } else if (val.match(/[0-9]+/)) {
+      defaultBranch.setIntPref(name, parseInt(val, 10));
+    } else if (val == "true" || val == "false") {
+      defaultBranch.setBoolPref(name, val == "true");
+    } else {
+      console.log("Unable to match preference type for value:", val);
+    }
+  });
+}
+
+this.DevToolsPreferences = {
+  loadPrefs: function () {
+    processPrefFile("chrome://devtools/content/preferences/devtools.js");
+    processPrefFile("chrome://devtools/content/preferences/debugger.js");
+    processPrefFile("chrome://devtools/content/webide/webide-prefs.js");
+  }
+};
--- a/devtools/client/responsive.html/test/unit/xpcshell.ini
+++ b/devtools/client/responsive.html/test/unit/xpcshell.ini
@@ -1,11 +1,11 @@
 [DEFAULT]
 tags = devtools
-head = head.js ../../../framework/test/shared-redux-head.js
+head = ../../../../shared/tests/shared-xpcshell-head.js head.js ../../../framework/test/shared-redux-head.js
 firefox-appdir = browser
 
 [test_add_device.js]
 [test_add_device_type.js]
 [test_add_viewport.js]
 [test_change_device.js]
 [test_change_display_pixel_ratio.js]
 [test_change_location.js]
--- a/devtools/client/shared/redux/middleware/test/xpcshell.ini
+++ b/devtools/client/shared/redux/middleware/test/xpcshell.ini
@@ -1,9 +1,9 @@
 [DEFAULT]
 tags = devtools
-head = head.js
+head = ../../../../../shared/tests/shared-xpcshell-head.js  head.js
 firefox-appdir = browser
 skip-if = toolkit == 'android'
 
 [test_middleware-task-01.js]
 [test_middleware-task-02.js]
 [test_middleware-task-03.js]
--- a/devtools/client/shared/test/unit/xpcshell.ini
+++ b/devtools/client/shared/test/unit/xpcshell.ini
@@ -1,11 +1,11 @@
 [DEFAULT]
 tags = devtools
-head =
+head = ../../../../shared/tests/shared-xpcshell-head.js
 firefox-appdir = browser
 skip-if = toolkit == 'android'
 
 support-files =
   ../helper_color_data.js
 
 [test_advanceValidate.js]
 [test_attribute-parsing-01.js]
--- a/devtools/client/shared/vendor/stringvalidator/tests/unit/xpcshell.ini
+++ b/devtools/client/shared/vendor/stringvalidator/tests/unit/xpcshell.ini
@@ -1,8 +1,8 @@
 [DEFAULT]
 tags = devtools
-head = head_stringvalidator.js
+head = ../../../../../../shared/tests/shared-xpcshell-head.js head_stringvalidator.js
 firefox-appdir = browser
 skip-if = toolkit == 'android'
 
 [test_sanitizers.js]
 [test_validators.js]
--- a/devtools/client/sourceeditor/tern/tests/unit/xpcshell.ini
+++ b/devtools/client/sourceeditor/tern/tests/unit/xpcshell.ini
@@ -1,7 +1,7 @@
 [DEFAULT]
 tags = devtools
-head = head_tern.js
+head = ../../../../../shared/tests/shared-xpcshell-head.js  head_tern.js
 firefox-appdir = browser
 
 [test_autocompletion.js]
 [test_import_tern.js]
--- a/devtools/client/webconsole/net/test/unit/xpcshell.ini
+++ b/devtools/client/webconsole/net/test/unit/xpcshell.ini
@@ -1,8 +1,8 @@
 [DEFAULT]
 tags = devtools
-head =
+head = ../../../../../shared/tests/shared-xpcshell-head.js
 firefox-appdir = browser
 skip-if = toolkit == 'android'
 
 [test_json-utils.js]
 [test_net-utils.js]
--- a/devtools/platform/tests/unit/xpcshell.ini
+++ b/devtools/platform/tests/unit/xpcshell.ini
@@ -1,6 +1,7 @@
 [DEFAULT]
 tags = devtools
+head = ../../../shared/tests/shared-xpcshell-head.js
 firefox-appdir = browser
 skip-if = toolkit == 'android'
 
 [test_nsjsinspector.js]
--- a/devtools/server/tests/unit/xpcshell.ini
+++ b/devtools/server/tests/unit/xpcshell.ini
@@ -1,11 +1,11 @@
 [DEFAULT]
 tags = devtools
-head = head_dbg.js
+head = ../../../shared/tests/shared-xpcshell-head.js head_dbg.js
 firefox-appdir = browser
 skip-if = toolkit == 'android'
 
 support-files =
   babel_and_browserify_script_with_source_map.js
   source-map-data/sourcemapped.coffee
   source-map-data/sourcemapped.map
   post_init_global_actors.js
--- a/devtools/shared/acorn/tests/unit/xpcshell.ini
+++ b/devtools/shared/acorn/tests/unit/xpcshell.ini
@@ -1,9 +1,9 @@
 [DEFAULT]
 tags = devtools
-head = head_acorn.js
+head = ../../../tests/shared-xpcshell-head.js  head_acorn.js
 firefox-appdir = browser
 skip-if = toolkit == 'android'
 
 [test_import_acorn.js]
 [test_same_ast.js]
 [test_lenient_parser.js]
--- a/devtools/shared/discovery/tests/unit/xpcshell.ini
+++ b/devtools/shared/discovery/tests/unit/xpcshell.ini
@@ -1,6 +1,6 @@
 [DEFAULT]
 tags = devtools
-head =
+head = ../../../tests/shared-xpcshell-head.js
 firefox-appdir = browser
 
 [test_discovery.js]
--- a/devtools/shared/heapsnapshot/tests/unit/xpcshell.ini
+++ b/devtools/shared/heapsnapshot/tests/unit/xpcshell.ini
@@ -1,11 +1,11 @@
 [DEFAULT]
 tags = devtools heapsnapshot devtools-memory
-head = head_heapsnapshot.js
+head = ../../../tests/shared-xpcshell-head.js  head_heapsnapshot.js
 firefox-appdir = browser
 skip-if = toolkit == 'android'
 
 support-files =
   Census.jsm
   dominator-tree-worker.js
   heap-snapshot-worker.js
   Match.jsm
--- a/devtools/shared/jsbeautify/tests/unit/xpcshell.ini
+++ b/devtools/shared/jsbeautify/tests/unit/xpcshell.ini
@@ -1,7 +1,7 @@
 [DEFAULT]
 tags = devtools
-head = head_jsbeautify.js
+head = ../../../tests/shared-xpcshell-head.js  head_jsbeautify.js
 firefox-appdir = browser
 skip-if = toolkit == 'android'
 
 [test.js]
--- a/devtools/shared/moz.build
+++ b/devtools/shared/moz.build
@@ -1,16 +1,20 @@
 # -*- Mode: python; 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/.
 
 include('../templates.mozbuild')
 
+TEST_HARNESS_FILES.xpcshell.devtools.shared.tests += [
+    'tests/shared-xpcshell-head.js',
+]
+
 DIRS += [
     'acorn',
     'apps',
     'client',
     'css',
     'discovery',
     'fronts',
     'gcli',
@@ -62,10 +66,10 @@ DevToolsModules(
     'plural-form.js',
     'protocol.js',
     'system.js',
     'task.js',
     'ThreadSafeDevToolsUtils.js',
     'wasm-source-map.js',
 )
 
-with Files('**'):
-    BUG_COMPONENT = ('Firefox', 'Developer Tools')
+with Files('**'):
+    BUG_COMPONENT = ('Firefox', 'Developer Tools')
--- a/devtools/shared/performance/test/xpcshell.ini
+++ b/devtools/shared/performance/test/xpcshell.ini
@@ -1,7 +1,7 @@
 [DEFAULT]
 tags = devtools
-head = head.js
+head = ../../../shared/tests/shared-xpcshell-head.js  head.js
 firefox-appdir = browser
 skip-if = toolkit == 'android'
 
 [test_perf-utils-allocations-to-samples.js]
--- a/devtools/shared/platform/content/test/xpcshell.ini
+++ b/devtools/shared/platform/content/test/xpcshell.ini
@@ -1,6 +1,6 @@
 [DEFAULT]
 tags = devtools
-head =
+head = ../../../tests/shared-xpcshell-head.js
 firefox-appdir = browser
 
 [test_stack.js]
--- a/devtools/shared/pretty-fast/tests/unit/xpcshell.ini
+++ b/devtools/shared/pretty-fast/tests/unit/xpcshell.ini
@@ -1,7 +1,7 @@
 [DEFAULT]
 tags = devtools
-head = head_pretty-fast.js
+head = ../../../tests/shared-xpcshell-head.js  head_pretty-fast.js
 firefox-appdir = browser
 skip-if = toolkit == 'android'
 
 [test.js]
--- a/devtools/shared/qrcode/tests/unit/xpcshell.ini
+++ b/devtools/shared/qrcode/tests/unit/xpcshell.ini
@@ -1,6 +1,6 @@
 [DEFAULT]
 tags = devtools
-head =
+head = ../../../tests/shared-xpcshell-head.js
 firefox-appdir = browser
 
 [test_encode.js]
--- a/devtools/shared/security/tests/unit/xpcshell.ini
+++ b/devtools/shared/security/tests/unit/xpcshell.ini
@@ -1,11 +1,11 @@
 [DEFAULT]
 tags = devtools
-head = head_dbg.js
+head = ../../../tests/shared-xpcshell-head.js  head_dbg.js
 firefox-appdir = browser
 
 support-files=
   testactors.js
 
 [test_encryption.js]
 [test_oob_cert_auth.js]
 skip-if = (toolkit == 'android' && !debug) # Bug 1141544: Re-enable when buildbot tests are gone
new file mode 100644
--- /dev/null
+++ b/devtools/shared/tests/shared-xpcshell-head.js
@@ -0,0 +1,32 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+(() => {
+  const {interfaces: Ci, utils: Cu} = Components;
+  const {Services} = Cu.import("resource://gre/modules/Services.jsm", {});
+
+  // Load our bootstrap extension manifest so we can access our chrome/resource URIs.
+  const EXTENSION_ID = "devtools@mozilla.org";
+  let extensionDir = Services.dirsvc.get("GreD", Ci.nsIFile);
+  extensionDir.append("browser");
+  extensionDir.append("features");
+  extensionDir.append(EXTENSION_ID);
+  // If the unpacked extension doesn't exist, use the packed version.
+  if (!extensionDir.exists()) {
+    extensionDir = extensionDir.parent;
+    extensionDir.append(EXTENSION_ID + ".xpi");
+  }
+  Components.manager.addBootstrappedManifestLocation(extensionDir);
+
+  try {
+    // Load devtools preferences that should normally be loaded by bootstrap.js
+    let {DevToolsPreferences} =
+      Cu.import("chrome://devtools/content/preferences/DevToolsPreferences.jsm", {});
+    DevToolsPreferences.loadPrefs();
+  } catch (e) {
+    // Will throw in case we are loading a test in content process
+    // (e.g. test_saveHeapSnapshot_e10s_01.js)
+  }
+})();
--- a/devtools/shared/tests/unit/xpcshell.ini
+++ b/devtools/shared/tests/unit/xpcshell.ini
@@ -1,11 +1,11 @@
 [DEFAULT]
 tags = devtools
-head = head_devtools.js
+head = ../shared-xpcshell-head.js  head_devtools.js
 firefox-appdir = browser
 skip-if = toolkit == 'android'
 support-files =
   exposeLoader.js
 
 [test_assert.js]
 [test_csslexer.js]
 [test_css-properties-db.js]
--- a/devtools/shared/transport/tests/unit/xpcshell.ini
+++ b/devtools/shared/transport/tests/unit/xpcshell.ini
@@ -1,11 +1,11 @@
 [DEFAULT]
 tags = devtools
-head = head_dbg.js
+head = ../../../tests/shared-xpcshell-head.js  head_dbg.js
 firefox-appdir = browser
 skip-if = toolkit == 'android'
 
 support-files =
   testactors.js
   testactors-no-bulk.js
 
 [test_bulk_error.js]
--- a/devtools/shared/webconsole/test/unit/xpcshell.ini
+++ b/devtools/shared/webconsole/test/unit/xpcshell.ini
@@ -1,11 +1,11 @@
 [DEFAULT]
 tags = devtools
-head =
+head = ../../../tests/shared-xpcshell-head.js
 firefox-appdir = browser
 skip-if = toolkit == 'android'
 support-files =
 
 [test_js_property_provider.js]
 [test_network_helper.js]
 [test_security-info-certificate.js]
 [test_security-info-parser.js]