Bug 1267365 - move various flags out of DevToolsUtils and don't depend on that module so much r=tromey
authorJames Long <longster@gmail.com>
Fri, 05 Aug 2016 17:41:01 -0400
changeset 308382 1646172ac6e709f5abdecbd8edbb53a54392428a
parent 308381 ba6d10b9a178a5700620a48b77b152908d4f8781
child 308383 8fcb3d139854375a36aaa25239dd2c4136d482d2
push id20241
push userjlong@mozilla.com
push dateFri, 05 Aug 2016 21:41:17 +0000
treeherderfx-team@1646172ac6e7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstromey
bugs1267365
milestone51.0a1
Bug 1267365 - move various flags out of DevToolsUtils and don't depend on that module so much r=tromey
devtools/client/aboutdebugging/test/head.js
devtools/client/canvasdebugger/canvasdebugger.js
devtools/client/canvasdebugger/test/head.js
devtools/client/commandline/test/head.js
devtools/client/framework/test/browser_ignore_toolbox_network_requests.js
devtools/client/framework/test/shared-head.js
devtools/client/framework/toolbox-highlighter-utils.js
devtools/client/framework/toolbox.js
devtools/client/inspector/breadcrumbs.js
devtools/client/inspector/markup/test/head.js
devtools/client/inspector/test/head.js
devtools/client/memory/store.js
devtools/client/memory/test/chrome/head.js
devtools/client/memory/test/unit/head.js
devtools/client/netmonitor/netmonitor-view.js
devtools/client/netmonitor/test/head.js
devtools/client/performance/components/test/head.js
devtools/client/performance/legacy/front.js
devtools/client/performance/modules/logic/telemetry.js
devtools/client/performance/performance-controller.js
devtools/client/performance/test/head.js
devtools/client/projecteditor/test/head.js
devtools/client/responsive.html/components/browser.js
devtools/client/responsive.html/store.js
devtools/client/responsive.html/test/browser/head.js
devtools/client/responsive.html/test/unit/head.js
devtools/client/responsivedesign/responsivedesign.jsm
devtools/client/responsivedesign/test/head.js
devtools/client/scratchpad/scratchpad.js
devtools/client/scratchpad/test/head.js
devtools/client/shadereditor/test/head.js
devtools/client/shared/components/test/mochitest/head.js
devtools/client/shared/redux/middleware/history.js
devtools/client/shared/redux/middleware/test/head.js
devtools/client/shared/widgets/view-helpers.js
devtools/client/sourceeditor/test/head.js
devtools/client/webaudioeditor/test/head.js
devtools/client/webaudioeditor/views/context.js
devtools/client/webconsole/new-console-output/test/actions/head.js
devtools/client/webconsole/new-console-output/test/components/head.js
devtools/client/webconsole/new-console-output/test/store/head.js
devtools/client/webconsole/test/head.js
devtools/client/webide/test/head.js
devtools/server/actors/inspector.js
devtools/server/actors/script.js
devtools/server/main.js
devtools/server/tests/mochitest/test_inspector_getImageData-wait-for-load.html
devtools/server/tests/mochitest/test_inspector_getImageDataFromURL.html
devtools/shared/DevToolsUtils.js
devtools/shared/ThreadSafeDevToolsUtils.js
devtools/shared/flags.js
devtools/shared/heapsnapshot/tests/unit/head_heapsnapshot.js
devtools/shared/moz.build
devtools/shared/protocol.js
devtools/shared/tests/unit/head_devtools.js
devtools/shared/tests/unit/test_assert.js
devtools/shared/transport/packets.js
devtools/shared/transport/transport.js
devtools/shared/webconsole/network-monitor.js
--- a/devtools/client/aboutdebugging/test/head.js
+++ b/devtools/client/aboutdebugging/test/head.js
@@ -14,20 +14,19 @@
 // Load the shared-head file first.
 Services.scriptloader.loadSubScript(
   "chrome://mochitests/content/browser/devtools/client/framework/test/shared-head.js",
   this);
 
 const { AddonManager } = Cu.import("resource://gre/modules/AddonManager.jsm", {});
 const { Management } = Cu.import("resource://gre/modules/Extension.jsm", {});
 
-DevToolsUtils.testing = true;
-
+flags.testing = true;
 registerCleanupFunction(() => {
-  DevToolsUtils.testing = false;
+  flags.testing = false;
 });
 
 function* openAboutDebugging(page, win) {
   info("opening about:debugging");
   let url = "about:debugging";
   if (page) {
     url += "#" + page;
   }
--- a/devtools/client/canvasdebugger/canvasdebugger.js
+++ b/devtools/client/canvasdebugger/canvasdebugger.js
@@ -9,21 +9,22 @@ const { require } = Cu.import("resource:
 const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");
 const { SideMenuWidget } = require("resource://devtools/client/shared/widgets/SideMenuWidget.jsm");
 const promise = require("promise");
 const Services = require("Services");
 const EventEmitter = require("devtools/shared/event-emitter");
 const { CallWatcherFront } = require("devtools/shared/fronts/call-watcher");
 const { CanvasFront } = require("devtools/shared/fronts/canvas");
 const DevToolsUtils = require("devtools/shared/DevToolsUtils");
+const flags = require("devtools/shared/flags");
 const { LocalizationHelper } = require("devtools/client/shared/l10n");
 const { Heritage, WidgetMethods, setNamedTimeout, clearNamedTimeout,
         setConditionalTimeout } = require("devtools/client/shared/widgets/view-helpers");
 
-const CANVAS_ACTOR_RECORDING_ATTEMPT = DevToolsUtils.testing ? 500 : 5000;
+const CANVAS_ACTOR_RECORDING_ATTEMPT = flags.testing ? 500 : 5000;
 
 const { Task } = require("devtools/shared/task");
 
 XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
   "resource://gre/modules/PluralForm.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
   "resource://gre/modules/FileUtils.jsm");
--- a/devtools/client/canvasdebugger/test/head.js
+++ b/devtools/client/canvasdebugger/test/head.js
@@ -11,16 +11,17 @@ var Services = require("Services");
 var promise = require("promise");
 var { gDevTools } = require("devtools/client/framework/devtools");
 var { DebuggerClient } = require("devtools/shared/client/main");
 var { DebuggerServer } = require("devtools/server/main");
 var { CallWatcherFront } = require("devtools/shared/fronts/call-watcher");
 var { CanvasFront } = require("devtools/shared/fronts/canvas");
 var { setTimeout } = require("sdk/timers");
 var DevToolsUtils = require("devtools/shared/DevToolsUtils");
+var flags = require("devtools/shared/flags");
 var { TargetFactory } = require("devtools/client/framework/target");
 var { Toolbox } = require("devtools/client/framework/toolbox");
 var { isWebGLSupported } = require("devtools/client/shared/webgl-utils");
 var mm = null;
 
 const FRAME_SCRIPT_UTILS_URL = "chrome://devtools/content/shared/frame-script-utils.js";
 const EXAMPLE_URL = "http://example.com/browser/devtools/client/canvasdebugger/test/";
 const SET_TIMEOUT_URL = EXAMPLE_URL + "doc_settimeout.html";
@@ -41,21 +42,21 @@ const RAF_BEGIN_URL = EXAMPLE_URL + "doc
 var gEnableLogging = Services.prefs.getBoolPref("devtools.debugger.log");
 Services.prefs.setBoolPref("devtools.debugger.log", false);
 
 // All tests are asynchronous.
 waitForExplicitFinish();
 
 var gToolEnabled = Services.prefs.getBoolPref("devtools.canvasdebugger.enabled");
 
-DevToolsUtils.testing = true;
+flags.testing = true;
 
 registerCleanupFunction(() => {
   info("finish() was called, cleaning up...");
-  DevToolsUtils.testing = false;
+  flags.testing = false;
   Services.prefs.setBoolPref("devtools.debugger.log", gEnableLogging);
   Services.prefs.setBoolPref("devtools.canvasdebugger.enabled", gToolEnabled);
 
   // Some of yhese tests use a lot of memory due to GL contexts, so force a GC
   // to help fragmentation.
   info("Forcing GC after canvas debugger test.");
   Cu.forceGC();
 });
--- a/devtools/client/commandline/test/head.js
+++ b/devtools/client/commandline/test/head.js
@@ -2,26 +2,26 @@
  * 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/. */
 
 const TEST_BASE_HTTP = "http://example.com/browser/devtools/client/commandline/test/";
 const TEST_BASE_HTTPS = "https://example.com/browser/devtools/client/commandline/test/";
 
 var { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
 var { console } = require("resource://gre/modules/Console.jsm");
-var DevToolsUtils = require("devtools/shared/DevToolsUtils");
+var flags = require("devtools/shared/flags");
 
 // Import the GCLI test helper
 var testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
 Services.scriptloader.loadSubScript(testDir + "/helpers.js", this);
 Services.scriptloader.loadSubScript(testDir + "/mockCommands.js", this);
 
-DevToolsUtils.testing = true;
+flags.testing = true;
 SimpleTest.registerCleanupFunction(() => {
-  DevToolsUtils.testing = false;
+  flags.testing = false;
 });
 
 function whenDelayedStartupFinished(aWindow, aCallback) {
   Services.obs.addObserver(function observer(aSubject, aTopic) {
     if (aWindow == aSubject) {
       Services.obs.removeObserver(observer, aTopic);
       executeSoon(aCallback);
     }
--- a/devtools/client/framework/test/browser_ignore_toolbox_network_requests.js
+++ b/devtools/client/framework/test/browser_ignore_toolbox_network_requests.js
@@ -7,27 +7,27 @@
 
 // Test that network requests originating from the toolbox don't get recorded in
 // the network panel.
 
 add_task(function* () {
   // TODO: This test tries to verify the normal behavior of the netmonitor and
   // therefore needs to avoid the explicit check for tests. Bug 1167188 will
   // allow us to remove this workaround.
-  let isTesting = DevToolsUtils.testing;
-  DevToolsUtils.testing = false;
+  let isTesting = flags.testing;
+  flags.testing = false;
 
   let tab = yield addTab(URL_ROOT + "doc_viewsource.html");
   let target = TargetFactory.forTab(tab);
   let toolbox = yield gDevTools.showToolbox(target, "styleeditor");
   let panel = toolbox.getPanel("styleeditor");
 
   is(panel.UI.editors.length, 1, "correct number of editors opened");
 
   let monitor = yield toolbox.selectTool("netmonitor");
   let { RequestsMenu } = monitor.panelWin.NetMonitorView;
   is(RequestsMenu.itemCount, 0, "No network requests appear in the network panel");
 
   yield gDevTools.closeToolbox(target);
   tab = target = toolbox = panel = null;
   gBrowser.removeCurrentTab();
-  DevToolsUtils.testing = isTesting;
+  flags.testing = isTesting;
 });
--- a/devtools/client/framework/test/shared-head.js
+++ b/devtools/client/framework/test/shared-head.js
@@ -21,16 +21,17 @@ function scopedCuImport(path) {
 
 const {console} = scopedCuImport("resource://gre/modules/Console.jsm");
 const {ScratchpadManager} = scopedCuImport("resource://devtools/client/scratchpad/scratchpad-manager.jsm");
 const {loader, require} = scopedCuImport("resource://devtools/shared/Loader.jsm");
 
 const {gDevTools} = require("devtools/client/framework/devtools");
 const {TargetFactory} = require("devtools/client/framework/target");
 const DevToolsUtils = require("devtools/shared/DevToolsUtils");
+const flags = require("devtools/shared/flags");
 let promise = require("promise");
 let defer = require("devtools/shared/defer");
 const Services = require("Services");
 const {Task} = require("devtools/shared/task");
 const {KeyShortcuts} = require("devtools/client/shared/key-shortcuts");
 
 const TEST_DIR = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
 const CHROME_URL_ROOT = TEST_DIR + "/";
@@ -84,19 +85,19 @@ function getFrameScript() {
   let frameURL = "chrome://devtools/content/shared/frame-script-utils.js";
   mm.loadFrameScript(frameURL, false);
   SimpleTest.registerCleanupFunction(() => {
     mm = null;
   });
   return mm;
 }
 
-DevToolsUtils.testing = true;
+flags.testing = true;
 registerCleanupFunction(() => {
-  DevToolsUtils.testing = false;
+  flags.testing = false;
   Services.prefs.clearUserPref("devtools.dump.emit");
   Services.prefs.clearUserPref("devtools.toolbox.host");
   Services.prefs.clearUserPref("devtools.toolbox.previousHost");
 });
 
 registerCleanupFunction(function* cleanup() {
   while (gBrowser.tabs.length > 1) {
     yield closeTabAndToolbox(gBrowser.selectedTab);
--- a/devtools/client/framework/toolbox-highlighter-utils.js
+++ b/devtools/client/framework/toolbox-highlighter-utils.js
@@ -3,17 +3,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/. */
 
 "use strict";
 
 const promise = require("promise");
 const {Task} = require("devtools/shared/task");
-const DevToolsUtils = require("devtools/shared/DevToolsUtils");
+const flags = require("devtools/shared/flags");
 
 /**
  * Client-side highlighter shared module.
  * To be used by toolbox panels that need to highlight DOM elements.
  *
  * Highlighting and selecting elements is common enough that it needs to be at
  * toolbox level, accessible by any panel that needs it.
  * That's why the toolbox is the one that initializes the inspector and
@@ -245,25 +245,25 @@ exports.getHighlighterUtils = function (
   let gripToNodeFront = exported.gripToNodeFront = requireInspector(
   function* (grip) {
     return yield toolbox.walker.getNodeActorFromObjectActor(grip.actor);
   });
 
   /**
    * Hide the highlighter.
    * @param {Boolean} forceHide Only really matters in test mode (when
-   * DevToolsUtils.testing is true). In test mode, hovering over several nodes
+   * flags.testing is true). In test mode, hovering over several nodes
    * in the markup view doesn't hide/show the highlighter to ease testing. The
    * highlighter stays visible at all times, except when the mouse leaves the
    * markup view, which is when this param is passed to true
    * @return a promise that resolves when the highlighter is hidden
    */
   let unhighlight = exported.unhighlight = Task.async(
   function* (forceHide = false) {
-    forceHide = forceHide || !DevToolsUtils.testing;
+    forceHide = forceHide || !flags.testing;
 
     // Note that if isRemoteHighlightable is true, there's no need to hide the
     // highlighter as the walker uses setTimeout to hide it after some time
     if (isNodeFrontHighlighted && forceHide && toolbox.highlighter && isRemoteHighlightable()) {
       isNodeFrontHighlighted = false;
       yield toolbox.highlighter.hideBoxModel();
     }
 
--- a/devtools/client/framework/toolbox.js
+++ b/devtools/client/framework/toolbox.js
@@ -51,30 +51,32 @@ loader.lazyRequireGetter(this, "CommandU
 loader.lazyRequireGetter(this, "getHighlighterUtils",
   "devtools/client/framework/toolbox-highlighter-utils", true);
 loader.lazyRequireGetter(this, "Hosts",
   "devtools/client/framework/toolbox-hosts", true);
 loader.lazyRequireGetter(this, "Selection",
   "devtools/client/framework/selection", true);
 loader.lazyRequireGetter(this, "InspectorFront",
   "devtools/shared/fronts/inspector", true);
-loader.lazyRequireGetter(this, "DevToolsUtils",
-  "devtools/shared/DevToolsUtils");
+loader.lazyRequireGetter(this, "flags",
+  "devtools/shared/flags");
 loader.lazyRequireGetter(this, "showDoorhanger",
   "devtools/client/shared/doorhanger", true);
 loader.lazyRequireGetter(this, "createPerformanceFront",
   "devtools/shared/fronts/performance", true);
 loader.lazyRequireGetter(this, "system",
   "devtools/shared/system");
 loader.lazyRequireGetter(this, "getPreferenceFront",
   "devtools/shared/fronts/preference", true);
 loader.lazyRequireGetter(this, "KeyShortcuts",
   "devtools/client/shared/key-shortcuts", true);
 loader.lazyRequireGetter(this, "ZoomKeys",
   "devtools/client/shared/zoom-keys");
+loader.lazyRequireGetter(this, "settleAll",
+  "devtools/shared/ThreadSafeDevToolsUtils", "settleAll");
 
 loader.lazyGetter(this, "registerHarOverlay", () => {
   return require("devtools/client/netmonitor/har/toolbox-overlay").register;
 });
 
 // White-list buttons that can be toggled to prevent adding prefs for
 // addons that have manually inserted toolbarbuttons into DOM.
 // (By default, supported target is only local tab)
@@ -472,17 +474,17 @@ Toolbox.prototype = {
 
       // Lazily connect to the profiler here and don't wait for it to complete,
       // used to intercept console.profile calls before the performance tools are open.
       let performanceFrontConnection = this.initPerformance();
 
       // If in testing environment, wait for performance connection to finish,
       // so we don't have to explicitly wait for this in tests; ideally, all tests
       // will handle this on their own, but each have their own tear down function.
-      if (DevToolsUtils.testing) {
+      if (flags.testing) {
         yield performanceFrontConnection;
       }
 
       this.emit("ready");
     }.bind(this)).then(null, console.error.bind(console));
   },
 
   /**
@@ -1934,17 +1936,17 @@ Toolbox.prototype = {
           {showAllAnonymousContent: Services.prefs.getBoolPref("devtools.inspector.showAllAnonymousContent")}
         );
         this._selection = new Selection(this._walker);
 
         if (this.highlighterUtils.isRemoteHighlightable()) {
           this.walker.on("highlighter-ready", this._highlighterReady);
           this.walker.on("highlighter-hide", this._highlighterHidden);
 
-          let autohide = !DevToolsUtils.testing;
+          let autohide = !flags.testing;
           this._highlighter = yield this._inspector.getHighlighter(autohide);
         }
       }.bind(this));
     }
     return this._initInspector;
   },
 
   /**
@@ -2113,49 +2115,49 @@ Toolbox.prototype = {
       CommandUtils.destroyRequisition(this._requisition, this.target);
     }
     this._telemetry.toolClosed("toolbox");
     this._telemetry.destroy();
 
     // Finish all outstanding tasks (which means finish destroying panels and
     // then destroying the host, successfully or not) before destroying the
     // target.
-    this._destroyer = DevToolsUtils.settleAll(outstanding)
-                                   .catch(console.error)
-                                   .then(() => this.destroyHost())
-                                   .catch(console.error)
-                                   .then(() => {
-      // Targets need to be notified that the toolbox is being torn down.
-      // This is done after other destruction tasks since it may tear down
-      // fronts and the debugger transport which earlier destroy methods may
-      // require to complete.
-                                     if (!this._target) {
-                                       return null;
-                                     }
-                                     let target = this._target;
-                                     this._target = null;
-                                     this.highlighterUtils.release();
-                                     target.off("close", this.destroy);
-                                     return target.destroy();
-                                   }, console.error).then(() => {
-                                     this.emit("destroyed");
+    this._destroyer = settleAll(outstanding)
+        .catch(console.error)
+        .then(() => this.destroyHost())
+        .catch(console.error)
+        .then(() => {
+          // Targets need to be notified that the toolbox is being torn down.
+          // This is done after other destruction tasks since it may tear down
+          // fronts and the debugger transport which earlier destroy methods may
+          // require to complete.
+          if (!this._target) {
+            return null;
+          }
+          let target = this._target;
+          this._target = null;
+          this.highlighterUtils.release();
+          target.off("close", this.destroy);
+          return target.destroy();
+        }, console.error).then(() => {
+          this.emit("destroyed");
 
-      // Free _host after the call to destroyed in order to let a chance
-      // to destroyed listeners to still query toolbox attributes
-                                     this._host = null;
-                                     this._toolPanels.clear();
+          // Free _host after the call to destroyed in order to let a chance
+          // to destroyed listeners to still query toolbox attributes
+          this._host = null;
+          this._toolPanels.clear();
 
-      // Force GC to prevent long GC pauses when running tests and to free up
-      // memory in general when the toolbox is closed.
-                                     if (DevToolsUtils.testing) {
-                                       win.QueryInterface(Ci.nsIInterfaceRequestor)
-           .getInterface(Ci.nsIDOMWindowUtils)
-           .garbageCollect();
-                                     }
-                                   }).then(null, console.error);
+          // Force GC to prevent long GC pauses when running tests and to free up
+          // memory in general when the toolbox is closed.
+          if (flags.testing) {
+            win.QueryInterface(Ci.nsIInterfaceRequestor)
+              .getInterface(Ci.nsIDOMWindowUtils)
+              .garbageCollect();
+          }
+        }).then(null, console.error);
 
     let leakCheckObserver = ({wrappedJSObject: barrier}) => {
       // Make the leak detector wait until this toolbox is properly destroyed.
       barrier.client.addBlocker("DevTools: Wait until toolbox is destroyed",
                                 this._destroyer);
     };
 
     let topic = "shutdown-leaks-before-check";
--- a/devtools/client/inspector/breadcrumbs.js
+++ b/devtools/client/inspector/breadcrumbs.js
@@ -7,17 +7,16 @@
 "use strict";
 
 /* eslint-disable mozilla/reject-some-requires */
 const {Ci} = require("chrome");
 /* eslint-enable mozilla/reject-some-requires */
 const Services = require("Services");
 const promise = require("promise");
 const FocusManager = Services.focus;
-const {waitForTick} = require("devtools/shared/DevToolsUtils");
 
 const ELLIPSIS = Services.prefs.getComplexValue(
     "intl.ellipsis",
     Ci.nsIPrefLocalizedString).data;
 const MAX_LABEL_LENGTH = 40;
 
 const NS_XHTML = "http://www.w3.org/1999/xhtml";
 const SCROLL_REPEAT_MS = 100;
@@ -877,20 +876,22 @@ HTMLBreadcrumbs.prototype = {
       this.setCursor(idx);
     }
 
     let doneUpdating = this.inspector.updating("breadcrumbs");
 
     this.updateSelectors();
 
     // Make sure the selected node and its neighbours are visible.
-    waitForTick().then(() => {
-      this.scroll();
-      this.inspector.emit("breadcrumbs-updated", this.selection.nodeFront);
-      doneUpdating();
-    }, e => {
-      // Only log this as an error if we haven't been destroyed in the meantime.
-      if (!this.isDestroyed) {
-        console.error(e);
+    setTimeout(() => {
+      try {
+        this.scroll();
+        this.inspector.emit("breadcrumbs-updated", this.selection.nodeFront);
+        doneUpdating();
+      } catch (e) {
+        // Only log this as an error if we haven't been destroyed in the meantime.
+        if (!this.isDestroyed) {
+          console.error(e);
+        }
       }
-    });
+    }, 0);
   }
 };
--- a/devtools/client/inspector/markup/test/head.js
+++ b/devtools/client/inspector/markup/test/head.js
@@ -14,19 +14,19 @@ var {getInplaceEditorForSpan: inplaceEdi
 var clipboard = require("sdk/clipboard");
 var {ActorRegistryFront} = require("devtools/shared/fronts/actor-registry");
 
 // If a test times out we want to see the complete log and not just the last few
 // lines.
 SimpleTest.requestCompleteLog();
 
 // Set the testing flag on DevToolsUtils and reset it when the test ends
-DevToolsUtils.testing = true;
+flags.testing = true;
 registerCleanupFunction(() => {
-  DevToolsUtils.testing = false;
+  flags.testing = false;
 });
 
 // Clear preferences that may be set during the course of tests.
 registerCleanupFunction(() => {
   Services.prefs.clearUserPref("devtools.inspector.htmlPanelOpen");
   Services.prefs.clearUserPref("devtools.inspector.sidebarOpen");
   Services.prefs.clearUserPref("devtools.markup.pagesize");
   Services.prefs.clearUserPref("dom.webcomponents.enabled");
--- a/devtools/client/inspector/test/head.js
+++ b/devtools/client/inspector/test/head.js
@@ -29,19 +29,19 @@ Services.scriptloader.loadSubScript(
   "chrome://mochitests/content/browser/devtools/client/shared/test/test-actor-registry.js",
   this);
 
 // Import helpers for the inspector that are also shared with others
 Services.scriptloader.loadSubScript(
   "chrome://mochitests/content/browser/devtools/client/inspector/test/shared-head.js",
   this);
 
-DevToolsUtils.testing = true;
+flags.testing = true;
 registerCleanupFunction(() => {
-  DevToolsUtils.testing = false;
+  flags.testing = false;
 });
 
 registerCleanupFunction(() => {
   Services.prefs.clearUserPref("devtools.inspector.activeSidebar");
 });
 
 registerCleanupFunction(function* () {
   let target = TargetFactory.forTab(gBrowser.selectedTab);
--- a/devtools/client/memory/store.js
+++ b/devtools/client/memory/store.js
@@ -1,25 +1,25 @@
 /* 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/. */
 
 const { combineReducers } = require("../shared/vendor/redux");
 const createStore = require("../shared/redux/create-store");
 const reducers = require("./reducers");
 const { viewState } = require("./constants");
-const DevToolsUtils = require("devtools/shared/DevToolsUtils");
+const flags = require("devtools/shared/flags");
 
 module.exports = function () {
   let shouldLog = false;
   let history;
 
   // If testing, store the action history in an array
   // we'll later attach to the store
-  if (DevToolsUtils.testing) {
+  if (flags.testing) {
     history = [];
     // Uncomment this for TONS of logging in tests.
     // shouldLog = true;
   }
 
   let store = createStore({
     log: shouldLog,
     history
--- a/devtools/client/memory/test/chrome/head.js
+++ b/devtools/client/memory/test/chrome/head.js
@@ -18,18 +18,19 @@ var EXPECTED_DTU_ASSERT_FAILURE_COUNT = 
 SimpleTest.registerCleanupFunction(function () {
   if (DevToolsUtils.assertionFailureCount !== EXPECTED_DTU_ASSERT_FAILURE_COUNT) {
     ok(false, "Should have had the expected number of DevToolsUtils.assert() failures. Expected " +
        EXPECTED_DTU_ASSERT_FAILURE_COUNT + ", got " + DevToolsUtils.assertionFailureCount);
   }
 });
 
 var DevToolsUtils = require("devtools/shared/DevToolsUtils");
-DevToolsUtils.testing = true;
 var { immutableUpdate } = DevToolsUtils;
+var flags = require("devtools/shared/flags");
+flags.testing = true;
 
 var constants = require("devtools/client/memory/constants");
 var {
   censusDisplays,
   diffingState,
   labelDisplays,
   dominatorTreeState,
   snapshotState,
--- a/devtools/client/memory/test/unit/head.js
+++ b/devtools/client/memory/test/unit/head.js
@@ -4,19 +4,20 @@
 "use strict";
 
 var { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 var { console } = Cu.import("resource://gre/modules/Console.jsm", {});
 var { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
 
 var Services = require("Services");
 var DevToolsUtils = require("devtools/shared/DevToolsUtils");
-DevToolsUtils.testing = true;
-DevToolsUtils.dumpn.wantLogging = true;
-DevToolsUtils.dumpv.wantVerbose = false;
+var flags = require("devtools/shared/flags");
+flags.testing = true;
+flags.wantLogging = true;
+flags.wantVerbose = false;
 
 var { OS } = require("resource://gre/modules/osfile.jsm");
 var { FileUtils } = require("resource://gre/modules/FileUtils.jsm");
 var { TargetFactory } = require("devtools/client/framework/target");
 var promise = require("promise");
 var defer = require("devtools/shared/defer");
 var { Task } = require("devtools/shared/task");
 var { expectState } = require("devtools/server/actors/common");
--- a/devtools/client/netmonitor/netmonitor-view.js
+++ b/devtools/client/netmonitor/netmonitor-view.js
@@ -20,17 +20,17 @@ XPCOMUtils.defineLazyGetter(this, "Netwo
 
 const {SideMenuWidget} = require("resource://devtools/client/shared/widgets/SideMenuWidget.jsm");
 const {VariablesView} = require("resource://devtools/client/shared/widgets/VariablesView.jsm");
 const {VariablesViewController} = require("resource://devtools/client/shared/widgets/VariablesViewController.jsm");
 const {ToolSidebar} = require("devtools/client/framework/sidebar");
 const {HTMLTooltip} = require("devtools/client/shared/widgets/HTMLTooltip");
 const {setImageTooltip, getImageDimensions} =
   require("devtools/client/shared/widgets/tooltip/ImageTooltipHelper");
-const DevToolsUtils = require("devtools/shared/DevToolsUtils");
+const { testing: isTesting } = require("devtools/shared/flags");
 const {LocalizationHelper} = require("devtools/client/shared/l10n");
 const {PrefsHelper} = require("devtools/client/shared/prefs");
 const {ViewHelpers, Heritage, WidgetMethods, setNamedTimeout} =
   require("devtools/client/shared/widgets/view-helpers");
 const {gDevTools} = require("devtools/client/framework/devtools");
 
 /**
  * Localization convenience methods.
@@ -43,17 +43,17 @@ const WEBCONSOLE_L10N = new Localization
 // ms
 const WDA_DEFAULT_VERIFY_INTERVAL = 50;
 
 // Use longer timeout during testing as the tests need this process to succeed
 // and two seconds is quite short on slow debug builds. The timeout here should
 // be at least equal to the general mochitest timeout of 45 seconds so that this
 // never gets hit during testing.
 // ms
-const WDA_DEFAULT_GIVE_UP_TIMEOUT = DevToolsUtils.testing ? 45000 : 2000;
+const WDA_DEFAULT_GIVE_UP_TIMEOUT = isTesting ? 45000 : 2000;
 
 /**
  * Shortcuts for accessing various network monitor preferences.
  */
 var Prefs = new PrefsHelper("devtools.netmonitor", {
   networkDetailsWidth: ["Int", "panes-network-details-width"],
   networkDetailsHeight: ["Int", "panes-network-details-height"],
   statistics: ["Bool", "statistics"],
@@ -337,17 +337,17 @@ var NetMonitorView = {
         // • The response content size and request total time are necessary for
         // populating the statistics view.
         // • The response mime type is used for categorization.
         yield whenDataAvailable(requestsView.attachments, [
           "responseHeaders", "status", "contentSize", "mimeType", "totalTime"
         ]);
       } catch (ex) {
         // Timed out while waiting for data. Continue with what we have.
-        DevToolsUtils.reportException("showNetworkStatisticsView", ex);
+        console.error(ex);
       }
 
       statisticsView.createPrimedCacheChart(requestsView.items);
       statisticsView.createEmptyCacheChart(requestsView.items);
     });
   },
 
   reloadPage: function () {
--- a/devtools/client/netmonitor/test/head.js
+++ b/devtools/client/netmonitor/test/head.js
@@ -7,16 +7,17 @@ var { classes: Cc, interfaces: Ci, utils
 var { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
 var { gDevTools } = require("devtools/client/framework/devtools");
 var { Task } = require("devtools/shared/task");
 var { CurlUtils } = Cu.import("resource://devtools/client/shared/Curl.jsm", {});
 var Services = require("Services");
 var promise = require("promise");
 var NetworkHelper = require("devtools/shared/webconsole/network-helper");
 var DevToolsUtils = require("devtools/shared/DevToolsUtils");
+var flags = require("devtools/shared/flags");
 var { TargetFactory } = require("devtools/client/framework/target");
 var { Toolbox } = require("devtools/client/framework/toolbox");
 
 const EXAMPLE_URL = "http://example.com/browser/devtools/client/netmonitor/test/";
 
 const API_CALLS_URL = EXAMPLE_URL + "html_api-calls-test-page.html";
 const SIMPLE_URL = EXAMPLE_URL + "html_simple-test-page.html";
 const NAVIGATE_URL = EXAMPLE_URL + "html_navigate-test-page.html";
@@ -55,19 +56,19 @@ const HSTS_SJS = EXAMPLE_URL + "sjs_hsts
 const HSTS_BASE_URL = EXAMPLE_URL;
 const HSTS_PAGE_URL = CUSTOM_GET_URL;
 
 const TEST_IMAGE = EXAMPLE_URL + "test-image.png";
 const TEST_IMAGE_DATA_URI = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAHWSURBVHjaYvz//z8DJQAggJiQOe/fv2fv7Oz8rays/N+VkfG/iYnJfyD/1+rVq7ffu3dPFpsBAAHEAHIBCJ85c8bN2Nj4vwsDw/8zQLwKiO8CcRoQu0DxqlWrdsHUwzBAAIGJmTNnPgYa9j8UqhFElwPxf2MIDeIrKSn9FwSJoRkAEEAM0DD4DzMAyPi/G+QKY4hh5WAXGf8PDQ0FGwJ22d27CjADAAIIrLmjo+MXA9R2kAHvGBA2wwx6B8W7od6CeQcggKCmCEL8bgwxYCbUIGTDVkHDBia+CuotgACCueD3TDQN75D4xmAvCoK9ARMHBzAw0AECiBHkAlC0Mdy7x9ABNA3obAZXIAa6iKEcGlMVQHwWyjYuL2d4v2cPg8vZswx7gHyAAAK7AOif7SAbOqCmn4Ha3AHFsIDtgPq/vLz8P4MSkJ2W9h8ggBjevXvHDo4FQUQg/kdypqCg4H8lUIACnQ/SOBMYI8bAsAJFPcj1AAEEjwVQqLpAbXmH5BJjqI0gi9DTAAgDBBCcAVLkgmQ7yKCZxpCQxqUZhAECCJ4XgMl493ug21ZD+aDAXH0WLM4A9MZPXJkJIIAwTAR5pQMalaCABQUULttBGCCAGCnNzgABBgAMJ5THwGvJLAAAAABJRU5ErkJggg==";
 
 const FRAME_SCRIPT_UTILS_URL = "chrome://devtools/content/shared/frame-script-utils.js";
 
-DevToolsUtils.testing = true;
+flags.testing = true;
 SimpleTest.registerCleanupFunction(() => {
-  DevToolsUtils.testing = false;
+  flags.testing = false;
 });
 
 // All tests are asynchronous.
 waitForExplicitFinish();
 
 const gEnableLogging = Services.prefs.getBoolPref("devtools.debugger.log");
 // To enable logging for try runs, just set the pref to true.
 Services.prefs.setBoolPref("devtools.debugger.log", false);
--- a/devtools/client/performance/components/test/head.js
+++ b/devtools/client/performance/components/test/head.js
@@ -5,21 +5,22 @@
 
 var { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 
 var { require } = Cu.import("resource://gre/modules/devtools/shared/Loader.jsm", {});
 var { Assert } = require("resource://testing-common/Assert.jsm");
 var { BrowserLoader } = Cu.import("resource://devtools/client/shared/browser-loader.js", {});
 var defer = require("devtools/shared/defer");
 var DevToolsUtils = require("devtools/shared/DevToolsUtils");
+var flags = require("devtools/shared/flags");
 var { Task } = require("devtools/shared/task");
 var { TargetFactory } = require("devtools/client/framework/target");
 var { Toolbox } = require("devtools/client/framework/toolbox");
 
-DevToolsUtils.testing = true;
+flags.testing = true;
 var { require: browserRequire } = BrowserLoader({
   baseURI: "resource://devtools/client/performance/",
   window: this
 });
 
 var $ = (selector, scope = document) => scope.querySelector(selector);
 var $$ = (selector, scope = document) => scope.querySelectorAll(selector);
 
--- a/devtools/client/performance/legacy/front.js
+++ b/devtools/client/performance/legacy/front.js
@@ -9,17 +9,17 @@ const { Task } = require("devtools/share
 const Services = require("Services");
 const promise = require("promise");
 const { extend } = require("sdk/util/object");
 
 const Actors = require("devtools/client/performance/legacy/actors");
 const { LegacyPerformanceRecording } = require("devtools/client/performance/legacy/recording");
 const { importRecording } = require("devtools/client/performance/legacy/recording");
 const { normalizePerformanceFeatures } = require("devtools/shared/performance/recording-utils");
-const DevToolsUtils = require("devtools/shared/DevToolsUtils");
+const flags = require("devtools/shared/flags");
 const { getDeviceFront } = require("devtools/shared/device/device");
 const { getSystemInfo } = require("devtools/shared/system");
 const events = require("sdk/event/core");
 const { EventTarget } = require("sdk/event/target");
 const { Class } = require("sdk/core/heritage");
 
 /**
  * A connection to underlying actors (profiler, framerate, etc.)
@@ -424,17 +424,17 @@ const LegacyPerformanceFront = Class({
     events.emit(this, eventName, ...args);
   },
 
   /**
    * Helper method to interface with the underlying actors directly.
    * Used only in tests.
    */
   _request: function (actorName, method, ...args) {
-    if (!DevToolsUtils.testing) {
+    if (!flags.testing) {
       throw new Error("LegacyPerformanceFront._request may only be used in tests.");
     }
     let actor = this[`_${actorName}`];
     return actor[method].apply(actor, args);
   },
 
   /**
    * Sets how often the "profiler-status" event should be emitted.
--- a/devtools/client/performance/modules/logic/telemetry.js
+++ b/devtools/client/performance/modules/logic/telemetry.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 Telemetry = require("devtools/client/shared/telemetry");
-const DevToolsUtils = require("devtools/shared/DevToolsUtils");
+const flags = require("devtools/shared/flags");
 const EVENTS = require("devtools/client/performance/events");
 
 const EVENT_MAP_FLAGS = new Map([
   [EVENTS.RECORDING_IMPORTED, "DEVTOOLS_PERFTOOLS_RECORDING_IMPORT_FLAG"],
   [EVENTS.RECORDING_EXPORTED, "DEVTOOLS_PERFTOOLS_RECORDING_EXPORT_FLAG"],
 ]);
 
 const RECORDING_FEATURES = [
@@ -27,17 +27,17 @@ function PerformanceTelemetry(emitter) {
 
   for (let [event] of EVENT_MAP_FLAGS) {
     this._emitter.on(event, this.onFlagEvent);
   }
 
   this._emitter.on(EVENTS.RECORDING_STATE_CHANGE, this.onRecordingStateChange);
   this._emitter.on(EVENTS.UI_DETAILS_VIEW_SELECTED, this.onViewSelected);
 
-  if (DevToolsUtils.testing) {
+  if (flags.testing) {
     this.recordLogs();
   }
 }
 
 PerformanceTelemetry.prototype.destroy = function () {
   if (this._previousView) {
     this._telemetry.stopTimer(SELECTED_VIEW_HISTOGRAM_NAME, this._previousView);
   }
@@ -84,17 +84,17 @@ PerformanceTelemetry.prototype.onViewSel
   this._telemetry.startTimer(SELECTED_VIEW_HISTOGRAM_NAME);
 };
 
 /**
  * Utility to record histogram calls to this instance.
  * Should only be used in testing mode; throws otherwise.
  */
 PerformanceTelemetry.prototype.recordLogs = function () {
-  if (!DevToolsUtils.testing) {
+  if (!flags.testing) {
     throw new Error("Can only record telemetry logs in tests.");
   }
 
   let originalLog = this._telemetry.log;
   let originalLogKeyed = this._telemetry.logKeyed;
   this._log = {};
 
   this._telemetry.log = (function (histo, data) {
@@ -106,16 +106,16 @@ PerformanceTelemetry.prototype.recordLog
   this._telemetry.logKeyed = (function (histo, key, data) {
     let results = this._log[histo] = this._log[histo] || [];
     results.push([key, data]);
     originalLogKeyed(histo, key, data);
   }).bind(this);
 };
 
 PerformanceTelemetry.prototype.getLogs = function () {
-  if (!DevToolsUtils.testing) {
+  if (!flags.testing) {
     throw new Error("Can only get telemetry logs in tests.");
   }
 
   return this._log;
 };
 
 exports.PerformanceTelemetry = PerformanceTelemetry;
--- a/devtools/client/performance/performance-controller.js
+++ b/devtools/client/performance/performance-controller.js
@@ -30,16 +30,17 @@ Object.defineProperty(this, "EVENTS", {
    DevToolsUtils, system */
 var React = require("devtools/client/shared/vendor/react");
 var ReactDOM = require("devtools/client/shared/vendor/react-dom");
 var JITOptimizationsView = React.createFactory(require("devtools/client/performance/components/jit-optimizations"));
 var Services = require("Services");
 var promise = require("promise");
 var EventEmitter = require("devtools/shared/event-emitter");
 var DevToolsUtils = require("devtools/shared/DevToolsUtils");
+var flags = require("devtools/shared/flags");
 var system = require("devtools/shared/system");
 
 // Logic modules
 /* exported L10N, PerformanceTelemetry, TIMELINE_BLUEPRINT, RecordingUtils,
    OptimizationsGraph, GraphsController, WaterfallHeader, MarkerView, MarkerDetails,
    MarkerBlueprintUtils, WaterfallUtils, FrameUtils, CallView, ThreadNode, FrameNode */
 var { L10N } = require("devtools/client/performance/modules/global");
 var { PerformanceTelemetry } = require("devtools/client/performance/modules/logic/telemetry");
@@ -511,17 +512,17 @@ var PerformanceController = {
    *
    * @return {object}
    */
   getMultiprocessStatus: function () {
     // If testing, set both supported and enabled to true so we
     // have realtime rendering tests in non-e10s. This function is
     // overridden wholesale in tests when we want to test multiprocess support
     // specifically.
-    if (DevToolsUtils.testing) {
+    if (flags.testing) {
       return { supported: true, enabled: true };
     }
     let supported = system.constants.E10S_TESTING_ONLY;
     // This is only checked on tool startup -- requires a restart if
     // e10s subsequently enabled.
     let enabled = this._e10s;
     return { supported, enabled };
   },
--- a/devtools/client/performance/test/head.js
+++ b/devtools/client/performance/test/head.js
@@ -46,35 +46,35 @@ const rightMousedown = (node, win = wind
 
 // Shortcut for firing a key event, like "VK_UP", "VK_DOWN", etc.
 const key = (id, win = window) => {
   EventUtils.synthesizeKey(id, {}, win);
 };
 
 // Don't pollute global scope.
 (() => {
-  const DevToolsUtils = require("devtools/shared/DevToolsUtils");
+  const flags = require("devtools/shared/flags");
   const PrefUtils = require("devtools/client/performance/test/helpers/prefs");
 
-  DevToolsUtils.testing = true;
+  flags.testing = true;
 
   // Make sure all the prefs are reverted to their defaults once tests finish.
   let stopObservingPrefs = PrefUtils.whenUnknownPrefChanged("devtools.performance", pref => {
     ok(false, `Unknown pref changed: ${pref}. Please add it to test/helpers/prefs.js ` +
       "to make sure it's reverted to its default value when the tests finishes, " +
       "and avoid interfering with future tests.\n");
   });
 
   // By default, enable memory flame graphs for tests for now.
   // TODO: remove when we have flame charts via bug 1148663.
   Services.prefs.setBoolPref(PrefUtils.UI_ENABLE_MEMORY_FLAME_CHART, true);
 
   registerCleanupFunction(() => {
     info("finish() was called, cleaning up...");
-    DevToolsUtils.testing = false;
+    flags.testing = false;
 
     PrefUtils.rollbackPrefsToDefault();
     stopObservingPrefs();
 
     // Manually stop the profiler module at the end of all tests, to hopefully
     // avoid at least some leaks on OSX. Theoretically the module should never
     // be active at this point. We shouldn't have to do this, but rather
     // find and fix the leak in the module itself. Bug 1257439.
--- a/devtools/client/projecteditor/test/head.js
+++ b/devtools/client/projecteditor/test/head.js
@@ -6,31 +6,32 @@ var Cu = Components.utils;
 const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
 const {TargetFactory} = require("devtools/client/framework/target");
 const {console} = Cu.import("resource://gre/modules/Console.jsm", {});
 const promise = require("promise");
 const {FileUtils} = Cu.import("resource://gre/modules/FileUtils.jsm", {});
 const {NetUtil} = Cu.import("resource://gre/modules/NetUtil.jsm", {});
 const ProjectEditor = require("devtools/client/projecteditor/lib/projecteditor");
 const DevToolsUtils = require("devtools/shared/DevToolsUtils");
+const flags = require("devtools/shared/flags");
 
 const TEST_URL_ROOT = "http://mochi.test:8888/browser/devtools/client/projecteditor/test/";
 const SAMPLE_WEBAPP_URL = TEST_URL_ROOT + "/helper_homepage.html";
 var TEMP_PATH;
 var TEMP_FOLDER_NAME = "ProjectEditor" + (new Date().getTime());
 
 // All test are asynchronous
 waitForExplicitFinish();
 
 // Uncomment this pref to dump all devtools emitted events to the console.
 // Services.prefs.setBoolPref("devtools.dump.emit", true);
 
-// Set the testing flag on DevToolsUtils and reset it when the test ends
-DevToolsUtils.testing = true;
-registerCleanupFunction(() => DevToolsUtils.testing = false);
+// Set the testing flag and reset it when the test ends
+flags.testing = true;
+registerCleanupFunction(() => flags.testing = false);
 
 // Clear preferences that may be set during the course of tests.
 registerCleanupFunction(() => {
   // Services.prefs.clearUserPref("devtools.dump.emit");
   TEMP_PATH = null;
   TEMP_FOLDER_NAME = null;
 });
 
--- a/devtools/client/responsive.html/components/browser.js
+++ b/devtools/client/responsive.html/components/browser.js
@@ -2,17 +2,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* eslint-env browser */
 
 "use strict";
 
 const { Task } = require("devtools/shared/task");
-const DevToolsUtils = require("devtools/shared/DevToolsUtils");
+const flags = require("devtools/shared/flags");
 const { getToplevelWindow } = require("sdk/window/utils");
 const { DOM: dom, createClass, addons, PropTypes } =
   require("devtools/client/shared/vendor/react");
 
 const Types = require("../types");
 const e10s = require("../utils/e10s");
 const message = require("../utils/message");
 
@@ -91,17 +91,17 @@ module.exports = createClass({
 
     let browserWindow = getToplevelWindow(window);
     let requiresFloatingScrollbars =
       !browserWindow.matchMedia("(-moz-overlay-scrollbars)").matches;
 
     yield e10s.request(mm, "Start", {
       requiresFloatingScrollbars,
       // Tests expect events on resize to yield on various size changes
-      notifyOnResize: DevToolsUtils.testing,
+      notifyOnResize: flags.testing,
     });
   }),
 
   stopFrameScript: Task.async(function* () {
     let { onContentResize } = this;
     let browser = this.refs.browserContainer.querySelector("iframe.browser");
     let mm = browser.frameLoader.messageManager;
     e10s.off(mm, "OnContentResize", onContentResize);
--- a/devtools/client/responsive.html/store.js
+++ b/devtools/client/responsive.html/store.js
@@ -2,25 +2,25 @@
  * 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 { combineReducers } = require("devtools/client/shared/vendor/redux");
 const createStore = require("devtools/client/shared/redux/create-store");
 const reducers = require("./reducers");
-const DevToolsUtils = require("devtools/shared/DevToolsUtils");
+const flags = require("devtools/shared/flags");
 
 module.exports = function () {
   let shouldLog = false;
   let history;
 
   // If testing, store the action history in an array
   // we'll later attach to the store
-  if (DevToolsUtils.testing) {
+  if (flags.testing) {
     history = [];
     shouldLog = true;
   }
 
   let store = createStore({
     log: shouldLog,
     history
   })(combineReducers(reducers), {});
--- a/devtools/client/responsive.html/test/browser/head.js
+++ b/devtools/client/responsive.html/test/browser/head.js
@@ -36,24 +36,24 @@ const OPEN_DEVICE_MODAL_VALUE = "OPEN_DE
 
 const { _loadPreferredDevices } = require("devtools/client/responsive.html/actions/devices");
 const { getOwnerWindow } = require("sdk/tabs/utils");
 const asyncStorage = require("devtools/shared/async-storage");
 
 SimpleTest.requestCompleteLog();
 SimpleTest.waitForExplicitFinish();
 
-DevToolsUtils.testing = true;
+flags.testing = true;
 Services.prefs.clearUserPref("devtools.responsive.html.displayedDeviceList");
 Services.prefs.setCharPref("devtools.devices.url",
   TEST_URI_ROOT + "devices.json");
 Services.prefs.setBoolPref("devtools.responsive.html.enabled", true);
 
 registerCleanupFunction(() => {
-  DevToolsUtils.testing = false;
+  flags.testing = false;
   Services.prefs.clearUserPref("devtools.devices.url");
   Services.prefs.clearUserPref("devtools.responsive.html.enabled");
   Services.prefs.clearUserPref("devtools.responsive.html.displayedDeviceList");
   asyncStorage.removeItem("devtools.devices.url_cache");
 });
 
 // This depends on the "devtools.responsive.html.enabled" pref
 const { ResponsiveUIManager } = require("resource://devtools/client/responsivedesign/responsivedesign.jsm");
--- a/devtools/client/responsive.html/test/unit/head.js
+++ b/devtools/client/responsive.html/test/unit/head.js
@@ -8,12 +8,14 @@
 const { utils: Cu } = Components;
 const { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
 
 const promise = require("promise");
 const { Task } = require("devtools/shared/task");
 const Store = require("devtools/client/responsive.html/store");
 
 const DevToolsUtils = require("devtools/shared/DevToolsUtils");
-DevToolsUtils.testing = true;
+
+const flags = require("devtools/shared/flags");
+flags.testing = true;
 do_register_cleanup(() => {
-  DevToolsUtils.testing = false;
+  flags.testing = false;
 });
--- a/devtools/client/responsivedesign/responsivedesign.jsm
+++ b/devtools/client/responsivedesign/responsivedesign.jsm
@@ -9,16 +9,17 @@ const Cu = Components.utils;
 
 var {loader, require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
 var Telemetry = require("devtools/client/shared/telemetry");
 var {showDoorhanger} = require("devtools/client/shared/doorhanger");
 var {TouchEventSimulator} = require("devtools/shared/touch/simulator");
 var {Task} = require("devtools/shared/task");
 var promise = require("promise");
 var DevToolsUtils = require("devtools/shared/DevToolsUtils");
+var flags = require("devtools/shared/flags");
 var Services = require("Services");
 var EventEmitter = require("devtools/shared/event-emitter");
 var {ViewHelpers} = require("devtools/client/shared/widgets/view-helpers");
 var { LocalizationHelper } = require("devtools/client/shared/l10n");
 
 loader.lazyImporter(this, "SystemAppProxy",
                     "resource://gre/modules/SystemAppProxy.jsm");
 loader.lazyRequireGetter(this, "DebuggerClient",
@@ -220,17 +221,17 @@ ResponsiveUI.prototype = {
 
     let requiresFloatingScrollbars =
       !this.mainWindow.matchMedia("(-moz-overlay-scrollbars)").matches;
     let started = this.waitForMessage("ResponsiveMode:Start:Done");
     debug("SEND START");
     this.mm.sendAsyncMessage("ResponsiveMode:Start", {
       requiresFloatingScrollbars,
       // Tests expect events on resize to yield on various size changes
-      notifyOnResize: DevToolsUtils.testing,
+      notifyOnResize: flags.testing,
     });
     yield started;
 
     // Load Presets
     this.loadPresets();
 
     // Setup the UI
     this.container.setAttribute("responsivemode", "true");
@@ -342,17 +343,17 @@ ResponsiveUI.prototype = {
                 "min-width: 0;" +
                 "max-height: none;" +
                 "min-height: 0;";
     debug("RESET STACK SIZE");
     this.stack.setAttribute("style", style);
 
     // Wait for resize message before stopping in the child when testing,
     // but only if we should expect to still get a message.
-    if (DevToolsUtils.testing && this.tab.linkedBrowser.messageManager) {
+    if (flags.testing && this.tab.linkedBrowser.messageManager) {
       yield this.waitForMessage("ResponsiveMode:OnContentResize");
     }
 
     if (this.isResizing)
       this.stopResizing();
 
     // Remove listeners.
     this.menulist.removeEventListener("select", this.bound_presetSelected, true);
--- a/devtools/client/responsivedesign/test/head.js
+++ b/devtools/client/responsivedesign/test/head.js
@@ -7,19 +7,19 @@ let testDir = gTestPath.substr(0, gTestP
 // shared-head.js handles imports, constants, and utility functions
 let sharedHeadURI = testDir + "../../../framework/test/shared-head.js";
 Services.scriptloader.loadSubScript(sharedHeadURI, this);
 
 // Import the GCLI test helper
 let gcliHelpersURI = testDir + "../../../commandline/test/helpers.js";
 Services.scriptloader.loadSubScript(gcliHelpersURI, this);
 
-DevToolsUtils.testing = true;
+flags.testing = true;
 registerCleanupFunction(() => {
-  DevToolsUtils.testing = false;
+  flags.testing = false;
   Services.prefs.clearUserPref("devtools.responsiveUI.currentPreset");
   Services.prefs.clearUserPref("devtools.responsiveUI.customHeight");
   Services.prefs.clearUserPref("devtools.responsiveUI.customWidth");
   Services.prefs.clearUserPref("devtools.responsiveUI.presets");
   Services.prefs.clearUserPref("devtools.responsiveUI.rotate");
 });
 
 SimpleTest.requestCompleteLog();
--- a/devtools/client/scratchpad/scratchpad.js
+++ b/devtools/client/scratchpad/scratchpad.js
@@ -45,16 +45,17 @@ const VARIABLES_VIEW_URL = "chrome://dev
 
 const {require, loader} = Cu.import("resource://devtools/shared/Loader.jsm", {});
 
 const Editor = require("devtools/client/sourceeditor/editor");
 const TargetFactory = require("devtools/client/framework/target").TargetFactory;
 const EventEmitter = require("devtools/shared/event-emitter");
 const {DevToolsWorker} = require("devtools/shared/worker/worker");
 const DevToolsUtils = require("devtools/shared/DevToolsUtils");
+const flags = require("devtools/shared/flags");
 const promise = require("promise");
 const Services = require("Services");
 const {gDevTools} = require("devtools/client/framework/devtools");
 const {Heritage} = require("devtools/client/shared/widgets/view-helpers");
 
 const {XPCOMUtils} = require("resource://gre/modules/XPCOMUtils.jsm");
 const {NetUtil} = require("resource://gre/modules/NetUtil.jsm");
 const {ScratchpadManager} = require("resource://devtools/client/scratchpad/scratchpad-manager.jsm");
@@ -666,17 +667,17 @@ var Scratchpad = {
   /**
    * Get or create the worker that handles pretty printing.
    */
   get prettyPrintWorker() {
     if (!this._prettyPrintWorker) {
       this._prettyPrintWorker = new DevToolsWorker(
         "resource://devtools/server/actors/pretty-print-worker.js",
         { name: "pretty-print",
-          verbose: DevToolsUtils.dumpn.wantLogging }
+          verbose: flags.wantLogging }
       );
     }
     return this._prettyPrintWorker;
   },
 
   /**
    * Pretty print the source text inside the scratchpad.
    *
--- a/devtools/client/scratchpad/test/head.js
+++ b/devtools/client/scratchpad/test/head.js
@@ -6,24 +6,25 @@
 
 const {NetUtil} = Cu.import("resource://gre/modules/NetUtil.jsm", {});
 const {FileUtils} = Cu.import("resource://gre/modules/FileUtils.jsm", {});
 const {console} = Cu.import("resource://gre/modules/Console.jsm", {});
 const {ScratchpadManager} = Cu.import("resource://devtools/client/scratchpad/scratchpad-manager.jsm", {});
 const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
 const Services = require("Services");
 const DevToolsUtils = require("devtools/shared/DevToolsUtils");
+const flags = require("devtools/shared/flags");
 const promise = require("promise");
 
 
 var gScratchpadWindow; // Reference to the Scratchpad chrome window object
 
-DevToolsUtils.testing = true;
+flags.testing = true;
 SimpleTest.registerCleanupFunction(() => {
-  DevToolsUtils.testing = false;
+  flags.testing = false;
 });
 
 /**
  * Open a Scratchpad window.
  *
  * @param function aReadyCallback
  *        Optional. The function you want invoked when the Scratchpad instance
  *        is ready.
--- a/devtools/client/shadereditor/test/head.js
+++ b/devtools/client/shadereditor/test/head.js
@@ -9,16 +9,17 @@ var { Task } = require("devtools/shared/
 
 var Services = require("Services");
 var promise = require("promise");
 var { gDevTools } = require("devtools/client/framework/devtools");
 var { DebuggerClient } = require("devtools/shared/client/main");
 var { DebuggerServer } = require("devtools/server/main");
 var { WebGLFront } = require("devtools/shared/fronts/webgl");
 var DevToolsUtils = require("devtools/shared/DevToolsUtils");
+var flags = require("devtools/shared/flags");
 var { TargetFactory } = require("devtools/client/framework/target");
 var { Toolbox } = require("devtools/client/framework/toolbox");
 var { isWebGLSupported } = require("devtools/client/shared/webgl-utils");
 var mm = null;
 
 const FRAME_SCRIPT_UTILS_URL = "chrome://devtools/content/shared/frame-script-utils.js";
 const EXAMPLE_URL = "http://example.com/browser/devtools/client/shadereditor/test/";
 const SIMPLE_CANVAS_URL = EXAMPLE_URL + "doc_simple-canvas.html";
@@ -31,21 +32,21 @@ var gEnableLogging = Services.prefs.getB
 // To enable logging for try runs, just set the pref to true.
 Services.prefs.setBoolPref("devtools.debugger.log", false);
 
 // All tests are asynchronous.
 waitForExplicitFinish();
 
 var gToolEnabled = Services.prefs.getBoolPref("devtools.shadereditor.enabled");
 
-DevToolsUtils.testing = true;
+flags.testing = true;
 
 registerCleanupFunction(() => {
   info("finish() was called, cleaning up...");
-  DevToolsUtils.testing = false;
+  flags.testing = false;
   Services.prefs.setBoolPref("devtools.debugger.log", gEnableLogging);
   Services.prefs.setBoolPref("devtools.shadereditor.enabled", gToolEnabled);
 
   // These tests use a lot of memory due to GL contexts, so force a GC to help
   // fragmentation.
   info("Forcing GC after shadereditor test.");
   Cu.forceGC();
 });
--- a/devtools/client/shared/components/test/mochitest/head.js
+++ b/devtools/client/shared/components/test/mochitest/head.js
@@ -11,21 +11,22 @@ var { Assert } = require("resource://tes
 var { gDevTools } = require("devtools/client/framework/devtools");
 var { BrowserLoader } = Cu.import("resource://devtools/client/shared/browser-loader.js", {});
 var promise = require("promise");
 var defer = require("devtools/shared/defer");
 var Services = require("Services");
 var { DebuggerServer } = require("devtools/server/main");
 var { DebuggerClient } = require("devtools/shared/client/main");
 var DevToolsUtils = require("devtools/shared/DevToolsUtils");
+var flags = require("devtools/shared/flags");
 var { Task } = require("devtools/shared/task");
 var { TargetFactory } = require("devtools/client/framework/target");
 var { Toolbox } = require("devtools/client/framework/toolbox");
 
-DevToolsUtils.testing = true;
+flags.testing = true;
 var { require: browserRequire } = BrowserLoader({
   baseURI: "resource://devtools/client/shared/",
   window: this
 });
 
 let ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
 let React = browserRequire("devtools/client/shared/vendor/react");
 var TestUtils = React.addons.TestUtils;
--- a/devtools/client/shared/redux/middleware/history.js
+++ b/devtools/client/shared/redux/middleware/history.js
@@ -1,22 +1,22 @@
 /* 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 DevToolsUtils = require("devtools/shared/DevToolsUtils");
+const flags = require("devtools/shared/flags");
 
 /**
  * A middleware that stores every action coming through the store in the passed
  * in logging object. Should only be used for tests, as it collects all
  * action information, which will cause memory bloat.
  */
 exports.history = (log = []) => ({ dispatch, getState }) => {
-  if (!DevToolsUtils.testing) {
+  if (!flags.testing) {
     console.warn("Using history middleware stores all actions in state for " +
                  "testing and devtools is not currently running in test " +
                  "mode. Be sure this is intentional.");
   }
   return next => action => {
     log.push(action);
     next(action);
   };
--- a/devtools/client/shared/redux/middleware/test/head.js
+++ b/devtools/client/shared/redux/middleware/test/head.js
@@ -1,19 +1,19 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 var { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 var { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
-var DevToolsUtils = require("devtools/shared/DevToolsUtils");
+var flags = require("devtools/shared/flags");
 var promise = require("promise");
 var defer = require("devtools/shared/defer");
 
-DevToolsUtils.testing = true;
+flags.testing = true;
 
 function waitUntilState(store, predicate) {
   let deferred = defer();
   let unsubscribe = store.subscribe(check);
 
   function check() {
     if (predicate(store.getState())) {
       unsubscribe();
--- a/devtools/client/shared/widgets/view-helpers.js
+++ b/devtools/client/shared/widgets/view-helpers.js
@@ -1,18 +1,16 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
-const DevToolsUtils = require("devtools/shared/DevToolsUtils");
-const { Cu, Ci } = require("chrome");
-let { XPCOMUtils } = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {});
+const { Ci } = require("chrome");
 
 const PANE_APPEARANCE_DELAY = 50;
 const PAGE_SIZE_ITEM_COUNT_RATIO = 5;
 const WIDGET_FOCUSABLE_NODES = new Set(["vbox", "hbox"]);
 
 /**
  * Inheritance helpers from the addon SDK's core/heritage.
  * Remove these when all devtools are loadered.
@@ -337,16 +335,17 @@ const ViewHelpers = exports.ViewHelpers 
  * @param any attachment
  *        Some attached primitive/object.
  */
 function Item(ownerView, element, value, attachment) {
   this.ownerView = ownerView;
   this.attachment = attachment;
   this._value = value + "";
   this._prebuiltNode = element;
+  this._itemsByElement = new Map();
 }
 
 Item.prototype = {
   get value() {
     return this._value;
   },
   get target() {
     return this._target;
@@ -459,21 +458,16 @@ Item.prototype = {
 
   _value: "",
   _target: null,
   _prebuiltNode: null,
   finalize: null,
   attachment: null
 };
 
-// Creating maps thousands of times for widgets with a large number of children
-// fills up a lot of memory. Make sure these are instantiated only if needed.
-DevToolsUtils.defineLazyPrototypeGetter(Item.prototype, "_itemsByElement",
-                                        () => new Map());
-
 /**
  * Some generic Widget methods handling Item instances.
  * Iterable via "for (let childItem of wrappedView) { }".
  *
  * Usage:
  *   function MyView() {
  *     this.widget = new MyWidget(document.querySelector(".my-node"));
  *   }
@@ -526,19 +520,19 @@ const WidgetMethods = exports.WidgetMeth
    * Sets the element node or widget associated with this container.
    * @param nsIDOMNode | object widget
    */
   set widget(widget) {
     this._widget = widget;
 
     // Can't use a WeakMap for _itemsByValue because keys are strings, and
     // can't use one for _itemsByElement either, since it needs to be iterable.
-    XPCOMUtils.defineLazyGetter(this, "_itemsByValue", () => new Map());
-    XPCOMUtils.defineLazyGetter(this, "_itemsByElement", () => new Map());
-    XPCOMUtils.defineLazyGetter(this, "_stagedItems", () => []);
+    this._itemsByValue = new Map();
+    this._itemsByElement = new Map();
+    this._stagedItems = [];
 
     // Handle internal events emitted by the widget if necessary.
     if (ViewHelpers.isEventEmitter(widget)) {
       widget.on("keyPress", this._onWidgetKeyPress.bind(this));
       widget.on("mousePress", this._onWidgetMousePress.bind(this));
     }
   },
 
--- a/devtools/client/sourceeditor/test/head.js
+++ b/devtools/client/sourceeditor/test/head.js
@@ -4,20 +4,21 @@
 
 "use strict";
 
 const { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
 const { NetUtil } = require("resource://gre/modules/NetUtil.jsm");
 const Editor = require("devtools/client/sourceeditor/editor");
 const promise = require("promise");
 const DevToolsUtils = require("devtools/shared/DevToolsUtils");
+const flags = require("devtools/shared/flags");
 
-DevToolsUtils.testing = true;
+flags.testing = true;
 SimpleTest.registerCleanupFunction(() => {
-  DevToolsUtils.testing = false;
+  flags.testing = false;
 });
 
 /**
  * Open a new tab at a URL and call a callback on load
  */
 function addTab(url, callback) {
   waitForExplicitFinish();
 
--- a/devtools/client/webaudioeditor/test/head.js
+++ b/devtools/client/webaudioeditor/test/head.js
@@ -11,16 +11,17 @@ var { gDevTools } = require("devtools/cl
 var { TargetFactory } = require("devtools/client/framework/target");
 var { DebuggerServer } = require("devtools/server/main");
 var { generateUUID } = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator);
 
 var Promise = require("promise");
 var Services = require("Services");
 var { WebAudioFront } = require("devtools/shared/fronts/webaudio");
 var DevToolsUtils = require("devtools/shared/DevToolsUtils");
+var flags = require("devtools/shared/flags");
 var audioNodes = require("devtools/server/actors/utils/audionodes.json");
 var mm = null;
 
 const FRAME_SCRIPT_UTILS_URL = "chrome://devtools/content/shared/frame-script-utils.js";
 const EXAMPLE_URL = "http://example.com/browser/devtools/client/webaudioeditor/test/";
 const SIMPLE_CONTEXT_URL = EXAMPLE_URL + "doc_simple-context.html";
 const COMPLEX_CONTEXT_URL = EXAMPLE_URL + "doc_complex-context.html";
 const SIMPLE_NODES_URL = EXAMPLE_URL + "doc_simple-node-creation.html";
@@ -37,20 +38,20 @@ const AUTOMATION_URL = EXAMPLE_URL + "do
 var gEnableLogging = Services.prefs.getBoolPref("devtools.debugger.log");
 Services.prefs.setBoolPref("devtools.debugger.log", false);
 
 // All tests are asynchronous.
 waitForExplicitFinish();
 
 var gToolEnabled = Services.prefs.getBoolPref("devtools.webaudioeditor.enabled");
 
-DevToolsUtils.testing = true;
+flags.testing = true;
 
 registerCleanupFunction(() => {
-  DevToolsUtils.testing = false;
+  flags.testing = false;
   info("finish() was called, cleaning up...");
   Services.prefs.setBoolPref("devtools.debugger.log", gEnableLogging);
   Services.prefs.setBoolPref("devtools.webaudioeditor.enabled", gToolEnabled);
   Cu.forceGC();
 });
 
 /**
  * Call manually in tests that use frame script utils after initializing
--- a/devtools/client/webaudioeditor/views/context.js
+++ b/devtools/client/webaudioeditor/views/context.js
@@ -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/. */
 "use strict";
 
 /* import-globals-from ../includes.js */
 
 const { debounce } = require("sdk/lang/functional");
+const flags = require("devtools/shared/flags");
 
 // Globals for d3 stuff
 // Default properties of the graph on rerender
 const GRAPH_DEFAULTS = {
   translate: [20, 20],
   scale: 1
 };
 
@@ -224,17 +225,17 @@ var ContextView = {
       let currentNode = InspectorView.getCurrentAudioNode();
       if (currentNode) {
         this.focusNode(currentNode.id);
       }
 
       // Fire an event upon completed rendering, with extra information
       // if in testing mode only.
       let info = {};
-      if (DevToolsUtils.testing) {
+      if (flags.testing) {
         info = gAudioNodes.getInfo();
       }
       window.emit(EVENTS.UI_GRAPH_RENDERED, info.nodes, info.edges, info.paramEdges);
     });
 
     let layout = dagreD3.layout().rankDir("LR");
     renderer.layout(layout).run(graph, d3.select("#graph-target"));
 
--- a/devtools/client/webconsole/new-console-output/test/actions/head.js
+++ b/devtools/client/webconsole/new-console-output/test/actions/head.js
@@ -2,19 +2,20 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 var { utils: Cu } = Components;
 var { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
 
 var DevToolsUtils = require("devtools/shared/DevToolsUtils");
-DevToolsUtils.testing = true;
-DevToolsUtils.dumpn.wantLogging = true;
-DevToolsUtils.dumpv.wantVerbose = false;
+var flags = require("devtools/shared/flags");
+flags.testing = true;
+flags.wantLogging = true;
+flags.wantVerbose = false;
 
 // @TODO consolidate once we have a shared head. See #16
 const testPackets = new Map();
 testPackets.set("console.log", {
   "from": "server1.conn4.child1/consoleActor2",
   "type": "consoleAPICall",
   "message": {
     "arguments": [
--- a/devtools/client/webconsole/new-console-output/test/components/head.js
+++ b/devtools/client/webconsole/new-console-output/test/components/head.js
@@ -7,23 +7,24 @@
 "use strict";
 
 var { utils: Cu } = Components;
 
 var { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
 var { Assert } = require("resource://testing-common/Assert.jsm");
 var { BrowserLoader } = Cu.import("resource://devtools/client/shared/browser-loader.js", {});
 var DevToolsUtils = require("devtools/shared/DevToolsUtils");
+var flags = require("devtools/shared/flags");
 var { Task } = require("devtools/shared/task");
 var { DebuggerServer } = require("devtools/server/main");
 var { DebuggerClient } = require("devtools/shared/client/main");
 
 const Services = require("Services");
 
-DevToolsUtils.testing = true;
+flags.testing = true;
 var { require: browserRequire } = BrowserLoader({
   baseURI: "resource://devtools/client/webconsole/",
   window: this
 });
 
 let ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
 let React = browserRequire("devtools/client/shared/vendor/react");
 var TestUtils = React.addons.TestUtils;
--- a/devtools/client/webconsole/new-console-output/test/store/head.js
+++ b/devtools/client/webconsole/new-console-output/test/store/head.js
@@ -5,19 +5,20 @@
 
 "use strict";
 
 var { utils: Cu } = Components;
 var { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
 const Services = require("Services");
 
 var DevToolsUtils = require("devtools/shared/DevToolsUtils");
-DevToolsUtils.testing = true;
-DevToolsUtils.dumpn.wantLogging = true;
-DevToolsUtils.dumpv.wantVerbose = false;
+var flags = require("devtools/shared/flags");
+flags.testing = true;
+flags.wantLogging = true;
+flags.wantVerbose = false;
 
 const { storeFactory } = require("devtools/client/webconsole/new-console-output/store");
 
 const testPackets = new Map();
 testPackets.set("console.log", {
   "from": "server1.conn4.child1/consoleActor2",
   "type": "consoleAPICall",
   "message": {
--- a/devtools/client/webconsole/test/head.js
+++ b/devtools/client/webconsole/test/head.js
@@ -40,17 +40,17 @@ const GROUP_INDENT = 12;
 const WEBCONSOLE_STRINGS_URI = "chrome://devtools/locale/" +
                                "webconsole.properties";
 var WCUL10n = new WebConsoleUtils.L10n(WEBCONSOLE_STRINGS_URI);
 
 const DOCS_GA_PARAMS = "?utm_source=mozilla" +
                        "&utm_medium=firefox-console-errors" +
                        "&utm_campaign=default";
 
-DevToolsUtils.testing = true;
+flags.testing = true;
 
 function loadTab(url) {
   let deferred = promise.defer();
 
   let tab = gBrowser.selectedTab = gBrowser.addTab(url);
   let browser = gBrowser.getBrowserForTab(tab);
 
   browser.addEventListener("load", function onLoad() {
@@ -318,17 +318,17 @@ var finishTest = Task.async(function* ()
 
   let target = TargetFactory.forTab(gBrowser.selectedTab);
   yield gDevTools.closeToolbox(target);
 
   finish();
 });
 
 registerCleanupFunction(function* () {
-  DevToolsUtils.testing = false;
+  flags.testing = false;
 
   // Remove stored console commands in between tests
   yield asyncStorage.removeItem("webConsoleHistory");
 
   dumpConsoles();
 
   let browserConsole = HUDService.getBrowserConsole();
   if (browserConsole) {
--- a/devtools/client/webide/test/head.js
+++ b/devtools/client/webide/test/head.js
@@ -9,17 +9,18 @@ const { require } = Cu.import("resource:
 const { FileUtils } = require("resource://gre/modules/FileUtils.jsm");
 const { gDevTools } = require("devtools/client/framework/devtools");
 const promise = require("promise");
 const Services = require("Services");
 const { Task } = require("devtools/shared/task");
 const { AppProjects } = require("devtools/client/webide/modules/app-projects");
 const DevToolsUtils = require("devtools/shared/DevToolsUtils");
 const { DebuggerServer } = require("devtools/server/main");
-DevToolsUtils.testing = true;
+const flags = require("devtools/shared/flags");
+flags.testing = true;
 
 var TEST_BASE;
 if (window.location === "chrome://browser/content/browser.xul") {
   TEST_BASE = "chrome://mochitests/content/browser/devtools/client/webide/test/";
 } else {
   TEST_BASE = "chrome://mochitests/content/chrome/devtools/client/webide/test/";
 }
 
@@ -31,17 +32,17 @@ Services.prefs.setCharPref("devtools.web
 Services.prefs.setCharPref("devtools.webide.adbAddonURL", TEST_BASE + "addons/adbhelper-#OS#.xpi");
 Services.prefs.setCharPref("devtools.webide.adaptersAddonURL", TEST_BASE + "addons/fxdt-adapters-#OS#.xpi");
 Services.prefs.setCharPref("devtools.webide.templatesURL", TEST_BASE + "templates.json");
 Services.prefs.setCharPref("devtools.devices.url", TEST_BASE + "browser_devices.json");
 
 var registerCleanupFunction = registerCleanupFunction ||
                               SimpleTest.registerCleanupFunction;
 registerCleanupFunction(() => {
-  DevToolsUtils.testing = false;
+  flags.testing = false;
   Services.prefs.clearUserPref("devtools.webide.enabled");
   Services.prefs.clearUserPref("devtools.webide.enableLocalRuntime");
   Services.prefs.clearUserPref("devtools.webide.autoinstallADBHelper");
   Services.prefs.clearUserPref("devtools.webide.autoinstallFxdtAdapters");
   Services.prefs.clearUserPref("devtools.webide.busyTimeout");
   Services.prefs.clearUserPref("devtools.webide.lastSelectedProject");
   Services.prefs.clearUserPref("devtools.webide.lastConnectedRuntime");
 });
--- a/devtools/server/actors/inspector.js
+++ b/devtools/server/actors/inspector.js
@@ -129,16 +129,18 @@ var HELPER_SHEET = `
   }
 
   :-moz-devtools-highlighted {
     outline: 2px dashed #F06!important;
     outline-offset: -2px !important;
   }
 `;
 
+const flags = require("devtools/shared/flags");
+
 loader.lazyRequireGetter(this, "DevToolsUtils",
                          "devtools/shared/DevToolsUtils");
 
 loader.lazyRequireGetter(this, "AsyncUtils", "devtools/shared/async-utils");
 
 loader.lazyGetter(this, "DOMParser", function () {
   return Cc["@mozilla.org/xmlextras/domparser;1"]
            .createInstance(Ci.nsIDOMParser);
@@ -2997,17 +2999,17 @@ function allAnonymousContentTreeWalkerFi
 }
 
 /**
  * Returns a promise that is settled once the given HTMLImageElement has
  * finished loading.
  *
  * @param {HTMLImageElement} image - The image element.
  * @param {Number} timeout - Maximum amount of time the image is allowed to load
- * before the waiting is aborted. Ignored if DevToolsUtils.testing is set.
+ * before the waiting is aborted. Ignored if flags.testing is set.
  *
  * @return {Promise} that is fulfilled once the image has loaded. If the image
  * fails to load or the load takes too long, the promise is rejected.
  */
 function ensureImageLoaded(image, timeout) {
   let { HTMLImageElement } = image.ownerDocument.defaultView;
   if (!(image instanceof HTMLImageElement)) {
     return promise.reject("image must be an HTMLImageELement");
@@ -3024,17 +3026,17 @@ function ensureImageLoaded(image, timeou
   // Reject if loading fails.
   let onError = AsyncUtils.listenOnce(image, "error").then(() => {
     return promise.reject("Image '" + image.src + "' failed to load.");
   });
 
   // Don't timeout when testing. This is never settled.
   let onAbort = new Promise(() => {});
 
-  if (!DevToolsUtils.testing) {
+  if (!flags.testing) {
     // Tests are not running. Reject the promise after given timeout.
     onAbort = DevToolsUtils.waitForTime(timeout).then(() => {
       return promise.reject("Image '" + image.src + "' took too long to load.");
     });
   }
 
   // See which happens first.
   return promise.race([onLoad, onError, onAbort]);
--- a/devtools/server/actors/script.js
+++ b/devtools/server/actors/script.js
@@ -12,16 +12,17 @@ const { ActorPool, OriginalLocation, Gen
 const { BreakpointActor, setBreakpointAtEntryPoints } = require("devtools/server/actors/breakpoint");
 const { EnvironmentActor } = require("devtools/server/actors/environment");
 const { FrameActor } = require("devtools/server/actors/frame");
 const { ObjectActor, createValueGrip, longStringGrip } = require("devtools/server/actors/object");
 const { SourceActor, getSourceURL } = require("devtools/server/actors/source");
 const { DebuggerServer } = require("devtools/server/main");
 const { ActorClassWithSpec } = require("devtools/shared/protocol");
 const DevToolsUtils = require("devtools/shared/DevToolsUtils");
+const flags = require("devtools/shared/flags");
 const { assert, dumpn, update, fetch } = DevToolsUtils;
 const promise = require("promise");
 const xpcInspector = require("xpcInspector");
 const { DevToolsWorker } = require("devtools/shared/worker/worker");
 const object = require("sdk/util/object");
 const { threadSpec } = require("devtools/shared/specs/script");
 
 const { defer, resolve, reject, all } = promise;
@@ -504,17 +505,17 @@ const ThreadActor = ActorClassWithSpec(t
   },
 
   _prettyPrintWorker: null,
   get prettyPrintWorker() {
     if (!this._prettyPrintWorker) {
       this._prettyPrintWorker = new DevToolsWorker(
         "resource://devtools/server/actors/pretty-print-worker.js",
         { name: "pretty-print",
-          verbose: dumpn.wantLogging }
+          verbose: flags.wantLogging }
       );
     }
     return this._prettyPrintWorker;
   },
 
   /**
    * Keep track of all of the nested event loops we use to pause the debuggee
    * when we hit a breakpoint/debugger statement/etc in one place so we can
--- a/devtools/server/main.js
+++ b/devtools/server/main.js
@@ -11,16 +11,17 @@
 var { Ci, Cc, CC, Cu, Cr } = require("chrome");
 var Services = require("Services");
 var { ActorPool, OriginalLocation, RegisteredActorFactory,
       ObservedActorFactory } = require("devtools/server/actors/common");
 var { LocalDebuggerTransport, ChildDebuggerTransport, WorkerDebuggerTransport } =
   require("devtools/shared/transport/transport");
 var DevToolsUtils = require("devtools/shared/DevToolsUtils");
 var { dumpn, dumpv } = DevToolsUtils;
+var flags = require("devtools/shared/flags");
 var EventEmitter = require("devtools/shared/event-emitter");
 var Promise = require("promise");
 var SyncPromise = require("devtools/shared/deprecated-sync-thenables");
 
 DevToolsUtils.defineLazyGetter(this, "DebuggerSocket", () => {
   let { DebuggerSocket } = require("devtools/shared/security/socket");
   return DebuggerSocket;
 });
@@ -51,24 +52,24 @@ this.dumpv = dumpv;
 // object usage
 Object.defineProperty(this, "Components", {
   get() {
     return require("chrome").components;
   }
 });
 
 if (isWorker) {
-  dumpn.wantLogging = true;
-  dumpv.wantVerbose = true;
+  flags.wantLogging = true;
+  flags.wantVerbose = true;
 } else {
   const LOG_PREF = "devtools.debugger.log";
   const VERBOSE_PREF = "devtools.debugger.log.verbose";
 
-  dumpn.wantLogging = Services.prefs.getBoolPref(LOG_PREF);
-  dumpv.wantVerbose =
+  flags.wantLogging = Services.prefs.getBoolPref(LOG_PREF);
+  flags.wantVerbose =
     Services.prefs.getPrefType(VERBOSE_PREF) !== Services.prefs.PREF_INVALID &&
     Services.prefs.getBoolPref(VERBOSE_PREF);
 }
 
 function loadSubScript(url) {
   try {
     let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"]
                    .getService(Ci.mozIJSSubScriptLoader);
--- a/devtools/server/tests/mochitest/test_inspector_getImageData-wait-for-load.html
+++ b/devtools/server/tests/mochitest/test_inspector_getImageData-wait-for-load.html
@@ -13,19 +13,19 @@ https://bugzilla.mozilla.org/show_bug.cg
   <meta charset="utf-8">
   <title>Test for Bug 1192536</title>
 
   <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
   <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script>
   <script type="application/javascript;version=1.8">
 
-const DevToolsUtils = require("devtools/shared/DevToolsUtils");
-const wasTesting = DevToolsUtils.testing;
-SimpleTest.registerCleanupFunction(() => DevToolsUtils.testing = wasTesting);
+const flags = require("devtools/shared/flags");
+const wasTesting = flags.testing;
+SimpleTest.registerCleanupFunction(() => flags.testing = wasTesting);
 
 const PATH = "http://mochi.test:8888/chrome/devtools/server/tests/mochitest/";
 const BASE_IMAGE = PATH + "inspector-delay-image-response.sjs";
 const DELAYED_IMAGE = BASE_IMAGE + "?delay=300";
 const TIMEOUT_IMAGE = BASE_IMAGE + "?delay=50000";
 const NONEXISTENT_IMAGE = PATH + "this-does-not-exist.png";
 
 window.onload = function() {
@@ -54,42 +54,42 @@ addTest(function setup() {
       });
     }).then(runNextTest));
   });
 });
 
 addTest(function testTimeout() {
   info("Testing that the method aborts if the image takes too long to load.");
 
-  // imageToImageData() only times out when DTU.testing is not set.
-  DevToolsUtils.testing = false;
+  // imageToImageData() only times out when flags.testing is not set.
+  flags.testing = false;
 
   gImg.src = TIMEOUT_IMAGE;
 
   info("Calling getImageData().");
   ensureRejects(gNodeFront.getImageData(), "Timeout image").then(runNextTest);
 });
 
 addTest(function testNonExistentImage() {
   info("Testing that non-existent image causes a rejection.");
 
   // This test shouldn't hit the timeout.
-  DevToolsUtils.testing = true;
+  flags.testing = true;
 
   gImg.src = NONEXISTENT_IMAGE;
 
   info("Calling getImageData().");
   ensureRejects(gNodeFront.getImageData(), "Non-existent image").then(runNextTest);
 });
 
 addTest(function testDelayedImage() {
   info("Testing that the method waits for an image to load.");
 
   // This test shouldn't hit the timeout.
-  DevToolsUtils.testing = true;
+  flags.testing = true;
 
   gImg.src = DELAYED_IMAGE;
 
   info("Calling getImageData().");
   checkImageData(gNodeFront.getImageData()).then(runNextTest);
 });
 
 addTest(function cleanup() {
--- a/devtools/server/tests/mochitest/test_inspector_getImageDataFromURL.html
+++ b/devtools/server/tests/mochitest/test_inspector_getImageDataFromURL.html
@@ -12,19 +12,19 @@ https://bugzilla.mozilla.org/show_bug.cg
   <meta charset="utf-8">
   <title>Test for Bug 1192536</title>
 
   <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
   <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script>
   <script type="application/javascript;version=1.8">
 
-const DevToolsUtils = require("devtools/shared/DevToolsUtils");
-const wasTesting = DevToolsUtils.testing;
-SimpleTest.registerCleanupFunction(() => DevToolsUtils.testing = wasTesting);
+const flags = require("devtools/shared/flags");
+const wasTesting = flags.testing;
+SimpleTest.registerCleanupFunction(() => flags.testing = wasTesting);
 
 const PATH = "http://mochi.test:8888/chrome/devtools/server/tests/mochitest/";
 const BASE_IMAGE = PATH + "inspector-delay-image-response.sjs";
 const DELAYED_IMAGE = BASE_IMAGE + "?delay=300";
 const TIMEOUT_IMAGE = BASE_IMAGE + "?delay=50000";
 const NONEXISTENT_IMAGE = PATH + "this-does-not-exist.png";
 
 window.onload = function() {
@@ -41,38 +41,38 @@ addTest(function setup() {
     gInspector = InspectorFront(client, tab);
     runNextTest();
   });
 });
 
 addTest(function testTimeout() {
   info("Testing that the method aborts if the image takes too long to load.");
 
-  // imageToImageData() only times out when DTU.testing is not set.
-  DevToolsUtils.testing = false;
+  // imageToImageData() only times out when flags.testing is not set.
+  flags.testing = false;
 
   ensureRejects(gInspector.getImageDataFromURL(TIMEOUT_IMAGE),
     "Image that loads for too long").then(runNextTest);
 });
 
 addTest(function testNonExistentImage() {
   info("Testing that non-existent image causes a rejection.");
 
   // This test shouldn't hit the timeout.
-  DevToolsUtils.testing = true;
+  flags.testing = true;
 
   ensureRejects(gInspector.getImageDataFromURL(NONEXISTENT_IMAGE),
     "Non-existent image").then(runNextTest);
 });
 
 addTest(function testNormalImage() {
   info("Testing that the method waits for an image to load.");
 
   // This test shouldn't hit the timeout.
-  DevToolsUtils.testing = true;
+  flags.testing = true;
 
   checkImageData(gInspector.getImageDataFromURL(DELAYED_IMAGE)).then(runNextTest);
 });
 
 addTest(function cleanup() {
   delete gInspector;
   runNextTest();
 });
--- a/devtools/shared/DevToolsUtils.js
+++ b/devtools/shared/DevToolsUtils.js
@@ -5,16 +5,17 @@
 "use strict";
 
 /* General utilities used throughout devtools. */
 
 var { Ci, Cu, Cc, components } = require("chrome");
 var Services = require("Services");
 var promise = require("promise");
 var defer = require("devtools/shared/defer");
+var flags = require("./flags");
 
 loader.lazyRequireGetter(this, "FileUtils",
                          "resource://gre/modules/FileUtils.jsm", true);
 
 // Re-export the thread-safe utils.
 const ThreadSafeDevToolsUtils = require("./ThreadSafeDevToolsUtils.js");
 for (let key of Object.keys(ThreadSafeDevToolsUtils)) {
   exports[key] = ThreadSafeDevToolsUtils[key];
@@ -25,17 +26,17 @@ for (let key of Object.keys(ThreadSafeDe
  */
 exports.executeSoon = function executeSoon(aFn) {
   if (isWorker) {
     setImmediate(aFn);
   } else {
     let executor;
     // Only enable async stack reporting when DEBUG_JS_MODULES is set
     // (customized local builds) to avoid a performance penalty.
-    if (AppConstants.DEBUG_JS_MODULES || exports.testing) {
+    if (AppConstants.DEBUG_JS_MODULES || flags.testing) {
       let stack = components.stack;
       executor = () => {
         Cu.callFunctionWithAsyncStack(aFn, stack, "DevToolsUtils.executeSoon");
       };
     } else {
       executor = aFn;
     }
     Services.tm.mainThread.dispatch({
@@ -229,38 +230,30 @@ exports.isSafeJSObject = function isSafe
   if (Services.scriptSecurityManager.isSystemPrincipal(principal)) {
     return true; // allow chrome objects
   }
 
   return Cu.isXrayWrapper(aObj);
 };
 
 exports.dumpn = function dumpn(str) {
-  if (exports.dumpn.wantLogging) {
+  if (flags.wantLogging) {
     dump("DBG-SERVER: " + str + "\n");
   }
 };
 
-// We want wantLogging to be writable. The exports object is frozen by the
-// loader, so define it on dumpn instead.
-exports.dumpn.wantLogging = false;
-
 /**
  * A verbose logger for low-level tracing.
  */
 exports.dumpv = function (msg) {
-  if (exports.dumpv.wantVerbose) {
+  if (flags.wantVerbose) {
     exports.dumpn(msg);
   }
 };
 
-// We want wantLogging to be writable. The exports object is frozen by the
-// loader, so define it on dumpn instead.
-exports.dumpv.wantVerbose = false;
-
 /**
  * Defines a getter on a specified object that will be created upon first use.
  *
  * @param aObject
  *        The object to define the lazy getter on.
  * @param aName
  *        The name of the getter to define on aObject.
  * @param aLambda
@@ -313,25 +306,25 @@ function reallyAssert(condition, message
  * DevToolsUtils.assert(condition, message)
  *
  * @param Boolean condition
  * @param String message
  *
  * Assertions are enabled when any of the following are true:
  *   - This is a DEBUG_JS_MODULES build
  *   - This is a DEBUG build
- *   - DevToolsUtils.testing is set to true
+ *   - flags.testing is set to true
  *
  * If assertions are enabled, then `condition` is checked and if false-y, the
  * assertion failure is logged and then an error is thrown.
  *
  * If assertions are not enabled, then this function is a no-op.
  */
 Object.defineProperty(exports, "assert", {
-  get: () => (AppConstants.DEBUG || AppConstants.DEBUG_JS_MODULES || this.testing)
+  get: () => (AppConstants.DEBUG || AppConstants.DEBUG_JS_MODULES || flags.testing)
     ? reallyAssert
     : exports.noop,
 });
 
 /**
  * Defines a getter on a specified object for a module.  The module will not
  * be imported until first use.
  *
@@ -571,103 +564,16 @@ if (!this.isWorker) {
   // to fetch a URL. We need to enlist the help from the main thread here, by
   // issuing an rpc request, to fetch the URL on our behalf.
   exports.fetch = function (url, options) {
     return rpc("fetch", url, options);
   };
 }
 
 /**
- * Returns a promise that is resolved or rejected when all promises have settled
- * (resolved or rejected).
- *
- * This differs from Promise.all, which will reject immediately after the first
- * rejection, instead of waiting for the remaining promises to settle.
- *
- * @param values
- *        Iterable of promises that may be pending, resolved, or rejected. When
- *        when all promises have settled (resolved or rejected), the returned
- *        promise will be resolved or rejected as well.
- *
- * @return A new promise that is fulfilled when all values have settled
- *         (resolved or rejected). Its resolution value will be an array of all
- *         resolved values in the given order, or undefined if values is an
- *         empty array. The reject reason will be forwarded from the first
- *         promise in the list of given promises to be rejected.
- */
-exports.settleAll = values => {
-  if (values === null || typeof (values[Symbol.iterator]) != "function") {
-    throw new Error("settleAll() expects an iterable.");
-  }
-
-  let deferred = defer();
-
-  values = Array.isArray(values) ? values : [...values];
-  let countdown = values.length;
-  let resolutionValues = new Array(countdown);
-  let rejectionValue;
-  let rejectionOccurred = false;
-
-  if (!countdown) {
-    deferred.resolve(resolutionValues);
-    return deferred.promise;
-  }
-
-  function checkForCompletion() {
-    if (--countdown > 0) {
-      return;
-    }
-    if (!rejectionOccurred) {
-      deferred.resolve(resolutionValues);
-    } else {
-      deferred.reject(rejectionValue);
-    }
-  }
-
-  for (let i = 0; i < values.length; i++) {
-    let index = i;
-    let value = values[i];
-    let resolver = result => {
-      resolutionValues[index] = result;
-      checkForCompletion();
-    };
-    let rejecter = error => {
-      if (!rejectionOccurred) {
-        rejectionValue = error;
-        rejectionOccurred = true;
-      }
-      checkForCompletion();
-    };
-
-    if (value && typeof (value.then) == "function") {
-      value.then(resolver, rejecter);
-    } else {
-      // Given value is not a promise, forward it as a resolution value.
-      resolver(value);
-    }
-  }
-
-  return deferred.promise;
-};
-
-/**
- * When the testing flag is set, various behaviors may be altered from
- * production mode, typically to enable easier testing or enhanced debugging.
- */
-var testing = false;
-Object.defineProperty(exports, "testing", {
-  get: function () {
-    return testing;
-  },
-  set: function (state) {
-    testing = state;
-  }
-});
-
-/**
  * Open the file at the given path for reading.
  *
  * @param {String} filePath
  *
  * @returns Promise<nsIInputStream>
  */
 exports.openFileStream = function (filePath) {
   return new Promise((resolve, reject) => {
@@ -680,8 +586,34 @@ exports.openFileStream = function (fileP
           return;
         }
 
         resolve(stream);
       }
     );
   });
 };
+
+/*
+ * All of the flags have been moved to a different module. Make sure
+ * nobody is accessing them anymore, and don't write new code using
+ * them. We can remove this code after a while.
+ */
+function errorOnFlag(exports, name) {
+  Object.defineProperty(exports, name, {
+    get: () => {
+      const msg = `Cannot get the flag ${name}. ` +
+            `Use the "devtools/shared/flags" module instead`;
+      console.error(msg);
+      throw new Error(msg);
+    },
+    set: () => {
+      const msg = `Cannot set the flag ${name}. ` +
+            `Use the "devtools/shared/flags" module instead`;
+      console.error(msg);
+      throw new Error(msg);
+    }
+  });
+}
+
+errorOnFlag(exports, "testing");
+errorOnFlag(exports, "wantLogging");
+errorOnFlag(exports, "wantVerbose");
--- a/devtools/shared/ThreadSafeDevToolsUtils.js
+++ b/devtools/shared/ThreadSafeDevToolsUtils.js
@@ -256,8 +256,79 @@ exports.isSet = function (thing) {
  * recursively flatten all levels.
  *
  * @param {Array<Array<Any>>} lists
  * @return {Array<Any>}
  */
 exports.flatten = function (lists) {
   return Array.prototype.concat.apply([], lists);
 };
+
+/**
+ * Returns a promise that is resolved or rejected when all promises have settled
+ * (resolved or rejected).
+ *
+ * This differs from Promise.all, which will reject immediately after the first
+ * rejection, instead of waiting for the remaining promises to settle.
+ *
+ * @param values
+ *        Iterable of promises that may be pending, resolved, or rejected. When
+ *        when all promises have settled (resolved or rejected), the returned
+ *        promise will be resolved or rejected as well.
+ *
+ * @return A new promise that is fulfilled when all values have settled
+ *         (resolved or rejected). Its resolution value will be an array of all
+ *         resolved values in the given order, or undefined if values is an
+ *         empty array. The reject reason will be forwarded from the first
+ *         promise in the list of given promises to be rejected.
+ */
+exports.settleAll = values => {
+  if (values === null || typeof (values[Symbol.iterator]) != "function") {
+    throw new Error("settleAll() expects an iterable.");
+  }
+
+  return new Promise((resolve, reject) => {
+    values = Array.isArray(values) ? values : [...values];
+    let countdown = values.length;
+    let resolutionValues = new Array(countdown);
+    let rejectionValue;
+    let rejectionOccurred = false;
+
+    if (!countdown) {
+      resolve(resolutionValues);
+      return deferred.promise;
+    }
+
+    function checkForCompletion() {
+      if (--countdown > 0) {
+        return;
+      }
+      if (!rejectionOccurred) {
+        resolve(resolutionValues);
+      } else {
+        reject(rejectionValue);
+      }
+    }
+
+    for (let i = 0; i < values.length; i++) {
+      let index = i;
+      let value = values[i];
+      let resolver = result => {
+        resolutionValues[index] = result;
+        checkForCompletion();
+      };
+      let rejecter = error => {
+        if (!rejectionOccurred) {
+          rejectionValue = error;
+          rejectionOccurred = true;
+        }
+        checkForCompletion();
+      };
+
+      if (value && typeof (value.then) == "function") {
+        value.then(resolver, rejecter);
+      } else {
+        // Given value is not a promise, forward it as a resolution value.
+        resolver(value);
+      }
+    }
+  });
+};
new file mode 100644
--- /dev/null
+++ b/devtools/shared/flags.js
@@ -0,0 +1,21 @@
+
+/*
+ * Create a writable property by tracking it with a private variable.
+ * We cannot make a normal property writeable on `exports` because
+ * the module system freezes it.
+ */
+function makeWritableFlag(exports, name) {
+  let flag = false;
+  Object.defineProperty(exports, name, {
+    get: function () { return flag; },
+    set: function (state) { flag = state; }
+  });
+}
+
+makeWritableFlag(exports, "wantLogging");
+makeWritableFlag(exports, "wantVerbose");
+
+// When the testing flag is set, various behaviors may be altered from
+// production mode, typically to enable easier testing or enhanced
+// debugging.
+makeWritableFlag(exports, "testing");
--- a/devtools/shared/heapsnapshot/tests/unit/head_heapsnapshot.js
+++ b/devtools/shared/heapsnapshot/tests/unit/head_heapsnapshot.js
@@ -12,33 +12,34 @@ var CC = Components.Constructor;
 const { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
 const { Match } = Cu.import("resource://test/Match.jsm", {});
 const { Census } = Cu.import("resource://test/Census.jsm", {});
 const { addDebuggerToGlobal } =
   Cu.import("resource://gre/modules/jsdebugger.jsm", {});
 const { Task } = require("devtools/shared/task");
 
 const DevToolsUtils = require("devtools/shared/DevToolsUtils");
+const flags = require("devtools/shared/flags");
 const HeapAnalysesClient =
   require("devtools/shared/heapsnapshot/HeapAnalysesClient");
 const Services = require("Services");
 const { censusReportToCensusTreeNode } = require("devtools/shared/heapsnapshot/census-tree-node");
 const CensusUtils = require("devtools/shared/heapsnapshot/CensusUtils");
 const DominatorTreeNode = require("devtools/shared/heapsnapshot/DominatorTreeNode");
 const { deduplicatePaths } = require("devtools/shared/heapsnapshot/shortest-paths");
 const { LabelAndShallowSizeVisitor } = DominatorTreeNode;
 
 
 // Always log packets when running tests. runxpcshelltests.py will throw
 // the output away anyway, unless you give it the --verbose flag.
 if (Services.appInfo &&
     Services.appInfo.processType == Services.appInfo.PROCESS_TYPE_DEFAULT) {
   Services.prefs.setBoolPref("devtools.debugger.log", true);
 }
-DevToolsUtils.dumpn.wantLogging = true;
+flags.wantLogging = true;
 
 const SYSTEM_PRINCIPAL = Cc["@mozilla.org/systemprincipal;1"]
   .createInstance(Ci.nsIPrincipal);
 
 function dumpn(msg) {
   dump("HEAPSNAPSHOT-TEST: " + msg + "\n");
 }
 
--- a/devtools/shared/moz.build
+++ b/devtools/shared/moz.build
@@ -48,16 +48,17 @@ DevToolsModules(
     'css-parsing-utils.js',
     'css-properties-db.js',
     'defer.js',
     'deprecated-sync-thenables.js',
     'DevToolsUtils.js',
     'dom-node-constants.js',
     'dom-node-filter-constants.js',
     'event-emitter.js',
+    'flags.js',
     'indentation.js',
     'loader-plugin-raw.jsm',
     'Loader.jsm',
     'Parser.jsm',
     'path.js',
     'protocol.js',
     'system.js',
     'task.js',
--- a/devtools/shared/protocol.js
+++ b/devtools/shared/protocol.js
@@ -1,16 +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";
 
 var { Cu, components } = require("chrome");
-var DevToolsUtils = require("devtools/shared/DevToolsUtils");
 var Services = require("Services");
 var promise = require("promise");
 var defer = require("devtools/shared/defer");
 var {Class} = require("sdk/core/heritage");
 var {EventTarget} = require("sdk/event/target");
 var events = require("sdk/event/core");
 var object = require("sdk/util/object");
 
@@ -1198,17 +1197,17 @@ var Front = Class({
    */
   send: function (packet) {
     if (packet.to) {
       this.conn._transport.send(packet);
     } else {
       this.actor().then(actorID => {
         packet.to = actorID;
         this.conn._transport.send(packet);
-      }).then(null, e => DevToolsUtils.reportException("Front.prototype.send", e));
+      }).then(null, e => console.error(e));
     }
   },
 
   /**
    * Send a two-way request on the connection.
    */
   request: function (packet) {
     let deferred = defer();
--- a/devtools/shared/tests/unit/head_devtools.js
+++ b/devtools/shared/tests/unit/head_devtools.js
@@ -4,20 +4,21 @@
 "use strict";
 var Cc = Components.classes;
 var Ci = Components.interfaces;
 var Cu = Components.utils;
 var Cr = Components.results;
 
 const {require, DevToolsLoader, devtools} = Cu.import("resource://devtools/shared/Loader.jsm", {});
 const DevToolsUtils = require("devtools/shared/DevToolsUtils");
+const flags = require("devtools/shared/flags");
 
-DevToolsUtils.testing = true;
+flags.testing = true;
 do_register_cleanup(() => {
-  DevToolsUtils.testing = false;
+  flags.testing = false;
 });
 
 // Register a console listener, so console messages don't just disappear
 // into the ether.
 
 // If for whatever reason the test needs to post console errors that aren't
 // failures, set this to true.
 var ALLOW_CONSOLE_ERRORS = false;
--- a/devtools/shared/tests/unit/test_assert.js
+++ b/devtools/shared/tests/unit/test_assert.js
@@ -3,17 +3,17 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Test DevToolsUtils.assert
 
 ALLOW_CONSOLE_ERRORS = true;
 
 function run_test() {
   // Enable assertions.
-  DevToolsUtils.testing = true;
+  flags.testing = true;
 
   const { assert } = DevToolsUtils;
   equal(typeof assert, "function");
 
   try {
     assert(true, "this assertion should not fail");
   } catch (e) {
     // If you catch assertion failures in practice, I will hunt you down. I get
--- a/devtools/shared/transport/packets.js
+++ b/devtools/shared/transport/packets.js
@@ -22,16 +22,17 @@
  *     Returns true once the packet is done being read / written
  *   * destroy()
  *     Called to clean up at the end of use
  */
 
 const { Cc, Ci, Cu } = require("chrome");
 const DevToolsUtils = require("devtools/shared/DevToolsUtils");
 const { dumpn, dumpv } = DevToolsUtils;
+const flags = require("devtools/shared/flags");
 const StreamUtils = require("devtools/shared/transport/stream-utils");
 const promise = require("promise");
 const defer = require("devtools/shared/defer");
 
 DevToolsUtils.defineLazyGetter(this, "unicodeConverter", () => {
   const unicodeConverter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
                            .createInstance(Ci.nsIScriptableUnicodeConverter);
   unicodeConverter.charset = "UTF-8";
@@ -170,17 +171,17 @@ JSONPacket.prototype.read = function (st
     dumpn(msg);
     return;
   }
 
   this._transport._onJSONObjectReady(this._object);
 };
 
 JSONPacket.prototype._readData = function (stream, scriptableStream) {
-  if (dumpv.wantVerbose) {
+  if (flags.wantVerbose) {
     dumpv("Reading JSON data: _l: " + this.length + " dL: " +
           this._data.length + " sA: " + stream.available());
   }
   let bytesToRead = Math.min(this.length - this._data.length,
                              stream.available());
   this._data += scriptableStream.readBytes(bytesToRead);
   this._done = this._data.length === this.length;
 };
--- a/devtools/shared/transport/transport.js
+++ b/devtools/shared/transport/transport.js
@@ -20,16 +20,17 @@
     const Cu = Components.utils;
     const { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
     factory.call(this, require, this);
   }
 }).call(this, function (require, exports) {
   const { Cc, Cr, CC } = require("chrome");
   const DevToolsUtils = require("devtools/shared/DevToolsUtils");
   const { dumpn, dumpv } = DevToolsUtils;
+  const flags = require("devtools/shared/flags");
   const StreamUtils = require("devtools/shared/transport/stream-utils");
   const { Packet, JSONPacket, BulkPacket } =
   require("devtools/shared/transport/packets");
   const promise = require("promise");
   const defer = require("devtools/shared/defer");
   const EventEmitter = require("devtools/shared/event-emitter");
 
   DevToolsUtils.defineLazyGetter(this, "Pipe", () => {
@@ -433,22 +434,22 @@
      * PACKET_HEADER_MAX characters.
      * @return boolean
      *         True if we now have a complete header.
      */
     _readHeader: function () {
       let amountToRead = PACKET_HEADER_MAX - this._incomingHeader.length;
       this._incomingHeader +=
       StreamUtils.delimitedRead(this._scriptableInput, ":", amountToRead);
-      if (dumpv.wantVerbose) {
+      if (flags.wantVerbose) {
         dumpv("Header read: " + this._incomingHeader);
       }
 
       if (this._incomingHeader.endsWith(":")) {
-        if (dumpv.wantVerbose) {
+        if (flags.wantVerbose) {
           dumpv("Found packet header successfully: " + this._incomingHeader);
         }
         return true;
       }
 
       if (this._incomingHeader.length >= PACKET_HEADER_MAX) {
         throw new Error("Failed to parse packet header!");
       }
@@ -459,17 +460,17 @@
 
     /**
      * If the incoming packet is done, log it as needed and clear the buffer.
      */
     _flushIncoming: function () {
       if (!this._incoming.done) {
         return;
       }
-      if (dumpn.wantLogging) {
+      if (flags.wantLogging) {
         dumpn("Got: " + this._incoming);
       }
       this._destroyIncoming();
     },
 
     /**
      * Handler triggered by an incoming JSONPacket completing it's |read| method.
      * Delivers the packet to this.hooks.onPacket.
@@ -543,30 +544,30 @@
     /**
      * Transmit a message by directly calling the onPacket handler of the other
      * endpoint.
      */
     send: function (packet) {
       this.emit("send", packet);
 
       let serial = this._serial.count++;
-      if (dumpn.wantLogging) {
+      if (flags.wantLogging) {
         // Check 'from' first, as 'echo' packets have both.
         if (packet.from) {
           dumpn("Packet " + serial + " sent from " + uneval(packet.from));
         } else if (packet.to) {
           dumpn("Packet " + serial + " sent to " + uneval(packet.to));
         }
       }
       this._deepFreeze(packet);
       let other = this.other;
       if (other) {
         DevToolsUtils.executeSoon(DevToolsUtils.makeInfallible(() => {
           // Avoid the cost of JSON.stringify() when logging is disabled.
-          if (dumpn.wantLogging) {
+          if (flags.wantLogging) {
             dumpn("Received packet " + serial + ": " + JSON.stringify(packet, null, 2));
           }
           if (other.hooks) {
             other.emit("onPacket", packet);
             other.hooks.onPacket(packet);
           }
         }, "LocalDebuggerTransport instance's this.other.hooks.onPacket"));
       }
--- a/devtools/shared/webconsole/network-monitor.js
+++ b/devtools/shared/webconsole/network-monitor.js
@@ -9,16 +9,18 @@
 const {Cc, Ci, Cm, Cu, Cr, components} = require("chrome");
 const Services = require("Services");
 const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");
 
 loader.lazyRequireGetter(this, "NetworkHelper",
                          "devtools/shared/webconsole/network-helper");
 loader.lazyRequireGetter(this, "DevToolsUtils",
                          "devtools/shared/DevToolsUtils");
+loader.lazyRequireGetter(this, "flags",
+                         "devtools/shared/flags");
 loader.lazyImporter(this, "NetUtil", "resource://gre/modules/NetUtil.jsm");
 loader.lazyServiceGetter(this, "gActivityDistributor",
                          "@mozilla.org/network/http-activity-distributor;1",
                          "nsIHttpActivityDistributor");
 
 // /////////////////////////////////////////////////////////////////////////////
 // Network logging
 // /////////////////////////////////////////////////////////////////////////////
@@ -50,19 +52,19 @@ function matchRequest(channel, filters) 
   // Log everything if no filter is specified
   if (!filters.topFrame && !filters.window && !filters.appId) {
     return true;
   }
 
   // Ignore requests from chrome or add-on code when we are monitoring
   // content.
   // TODO: one particular test (browser_styleeditor_fetch-from-cache.js) needs
-  // the DevToolsUtils.testing check. We will move to a better way to serve
+  // the flags.testing check. We will move to a better way to serve
   // its needs in bug 1167188, where this check should be removed.
-  if (!DevToolsUtils.testing && channel.loadInfo &&
+  if (!flags.testing && channel.loadInfo &&
       channel.loadInfo.loadingDocument === null &&
       channel.loadInfo.loadingPrincipal ===
       Services.scriptSecurityManager.getSystemPrincipal()) {
     return false;
   }
 
   if (filters.window) {
     // Since frames support, this.window may not be the top level content