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 308500 1646172ac6e709f5abdecbd8edbb53a54392428a
parent 308499 ba6d10b9a178a5700620a48b77b152908d4f8781
child 308501 8fcb3d139854375a36aaa25239dd2c4136d482d2
push id30538
push userkwierso@gmail.com
push dateSun, 07 Aug 2016 07:16:49 +0000
treeherdermozilla-central@d42aacfe34af [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstromey
bugs1267365
milestone51.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 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