Bug 1273941 - replace uses of promise.defer in devtools/client/shared; r=jryans
authorTom Tromey <tom@tromey.com>
Thu, 09 Jun 2016 09:04:22 -0600
changeset 302530 c93918e6463ac250d76f11f0e93aa76d9f740237
parent 302529 51bc72f4ddcc7857749398cc8c9072587110858e
child 302531 65a02c905ec6c6265944085e4dcf72b8184ba029
push id30364
push userkwierso@gmail.com
push dateFri, 24 Jun 2016 20:31:56 +0000
treeherdermozilla-central@9dac1358aaad [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjryans
bugs1273941
milestone50.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 1273941 - replace uses of promise.defer in devtools/client/shared; r=jryans MozReview-Commit-ID: K5P4eB0XPT9
devtools/client/framework/test/shared-head.js
devtools/client/shared/AppCacheUtils.jsm
devtools/client/shared/components/test/mochitest/head.js
devtools/client/shared/developer-toolbar.js
devtools/client/shared/devices.js
devtools/client/shared/doorhanger.js
devtools/client/shared/frame-script-utils.js
devtools/client/shared/getjson.js
devtools/client/shared/redux/middleware/promise.js
devtools/client/shared/redux/middleware/test/head.js
devtools/client/shared/test/browser_flame-graph-05.js
devtools/client/shared/test/browser_inplace-editor-01.js
devtools/client/shared/test/browser_inplace-editor-02.js
devtools/client/shared/test/browser_mdn-docs-01.js
devtools/client/shared/test/browser_treeWidget_keyboard_interaction.js
devtools/client/shared/test/browser_treeWidget_mouse_interaction.js
devtools/client/shared/test/head.js
devtools/client/shared/test/test-actor-registry.js
devtools/client/shared/test/test-actor.js
devtools/client/shared/widgets/FlameGraph.js
devtools/client/shared/widgets/Graphs.js
devtools/client/shared/widgets/MdnDocsWidget.js
devtools/client/shared/widgets/Tooltip.js
devtools/client/shared/widgets/VariablesView.jsm
devtools/client/shared/widgets/VariablesViewController.jsm
--- 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");
 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 + "/";
 const URL_ROOT = CHROME_URL_ROOT.replace("chrome://mochitests/content/",
                                          "http://example.com/");
--- a/devtools/client/shared/AppCacheUtils.jsm
+++ b/devtools/client/shared/AppCacheUtils.jsm
@@ -28,16 +28,17 @@
 const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
 
 var { XPCOMUtils } = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {});
 var { NetUtil } = Cu.import("resource://gre/modules/NetUtil.jsm", {});
 var { LoadContextInfo } = Cu.import("resource://gre/modules/LoadContextInfo.jsm", {});
 var { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
 var Services = require("Services");
 var promise = require("promise");
+var defer = require("devtools/shared/defer");
 
 this.EXPORTED_SYMBOLS = ["AppCacheUtils"];
 
 function AppCacheUtils(documentOrUri) {
   this._parseManifest = this._parseManifest.bind(this);
 
   if (documentOrUri) {
     if (typeof documentOrUri == "string") {
@@ -50,17 +51,17 @@ function AppCacheUtils(documentOrUri) {
 }
 
 AppCacheUtils.prototype = {
   get cachePath() {
     return "";
   },
 
   validateManifest: function ACU_validateManifest() {
-    let deferred = promise.defer();
+    let deferred = defer();
     this.errors = [];
     // Check for missing manifest.
     this._getManifestURI().then(manifestURI => {
       this.manifestURI = manifestURI;
 
       if (!this.manifestURI) {
         this._addError(0, "noManifest");
         deferred.resolve(this.errors);
@@ -76,17 +77,17 @@ AppCacheUtils.prototype = {
         });
       });
     });
 
     return deferred.promise;
   },
 
   _parseManifest: function ACU__parseManifest(uriInfo) {
-    let deferred = promise.defer();
+    let deferred = defer();
     let manifestName = uriInfo.name;
     let manifestLastModified = new Date(uriInfo.responseHeaders["Last-Modified"]);
 
     if (uriInfo.charset.toLowerCase() != "utf-8") {
       this._addError(0, "notUTF8", uriInfo.charset);
     }
 
     if (uriInfo.mimeType != "text/cache-manifest") {
@@ -180,17 +181,17 @@ AppCacheUtils.prototype = {
     }
 
     return deferred.promise;
   },
 
   _getURIInfo: function ACU__getURIInfo(uri) {
     let inputStream = Cc["@mozilla.org/scriptableinputstream;1"]
                         .createInstance(Ci.nsIScriptableInputStream);
-    let deferred = promise.defer();
+    let deferred = defer();
     let buffer = "";
     var channel = NetUtil.newChannel({
       uri: uri,
       loadUsingSystemPrincipal: true,
       securityFlags: Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL
     });
 
     // Avoid the cache:
@@ -305,17 +306,17 @@ AppCacheUtils.prototype = {
 
     let appCacheStorage = Services.cache2.appCacheStorage(LoadContextInfo.default, null);
     appCacheStorage.asyncEvictStorage({
       onCacheEntryDoomed: function (result) {}
     });
   },
 
   _getManifestURI: function ACU__getManifestURI() {
-    let deferred = promise.defer();
+    let deferred = defer();
 
     let getURI = () => {
       let htmlNode = this.doc.querySelector("html[manifest]");
       if (htmlNode) {
         let pageUri = this.doc.location ? this.doc.location.href : this.uri;
         let origin = pageUri.substr(0, pageUri.lastIndexOf("/") + 1);
         let manifestURI = htmlNode.getAttribute("manifest");
 
--- a/devtools/client/shared/components/test/mochitest/head.js
+++ b/devtools/client/shared/components/test/mochitest/head.js
@@ -4,16 +4,17 @@
 
 var { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 
 var { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
 var { Assert } = require("resource://testing-common/Assert.jsm");
 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 { Task } = require("devtools/shared/task");
 var { TargetFactory } = require("devtools/client/framework/target");
 var { Toolbox } = require("devtools/client/framework/toolbox");
 
@@ -39,23 +40,23 @@ SimpleTest.waitForExplicitFinish();
 
 function onNextAnimationFrame(fn) {
   return () =>
     requestAnimationFrame(() =>
       requestAnimationFrame(fn));
 }
 
 function setState(component, newState) {
-  var deferred = promise.defer();
+  var deferred = defer();
   component.setState(newState, onNextAnimationFrame(deferred.resolve));
   return deferred.promise;
 }
 
 function setProps(component, newState) {
-  var deferred = promise.defer();
+  var deferred = defer();
   component.setProps(newState, onNextAnimationFrame(deferred.resolve));
   return deferred.promise;
 }
 
 function dumpn(msg) {
   dump(`SHARED-COMPONENTS-TEST: ${msg}\n`);
 }
 
--- a/devtools/client/shared/developer-toolbar.js
+++ b/devtools/client/shared/developer-toolbar.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";
 
 const { Cc, Ci, Cu } = require("chrome");
 const promise = require("promise");
+const defer = require("devtools/shared/defer");
 const Services = require("Services");
 const { TargetFactory } = require("devtools/client/framework/target");
 const Telemetry = require("devtools/client/shared/telemetry");
 
 const NS_XHTML = "http://www.w3.org/1999/xhtml";
 const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 
 loader.lazyImporter(this, "PluralForm", "resource://gre/modules/PluralForm.jsm");
@@ -915,17 +916,17 @@ OutputPanel.prototype._init = function (
   this._frame.setAttribute("sandbox", "allow-same-origin");
   this._panel.appendChild(this._frame);
 
   this.displayedOutput = undefined;
 
   this._update = this._update.bind(this);
 
   // Wire up the element from the iframe, and resolve the promise
-  let deferred = promise.defer();
+  let deferred = defer();
   let onload = () => {
     this._frame.removeEventListener("load", onload, true);
 
     this.document = this._frame.contentDocument;
     this._copyTheme();
 
     this._div = this.document.getElementById("gcli-output-root");
     this._div.classList.add("gcli-row-out");
@@ -1184,17 +1185,17 @@ TooltipPanel.create = function (devtoolb
   var tooltipPanel = Object.create(TooltipPanel.prototype);
   return tooltipPanel._init(devtoolbar);
 };
 
 /**
  * @private See TooltipPanel.create
  */
 TooltipPanel.prototype._init = function (devtoolbar) {
-  let deferred = promise.defer();
+  let deferred = defer();
 
   let chromeDocument = devtoolbar._doc;
   this._devtoolbar = devtoolbar;
   this._input = devtoolbar._doc.querySelector(".gclitoolbar-input-node");
   this._toolbar = devtoolbar._doc.querySelector("#developer-toolbar");
   this._dimensions = { start: 0, end: 0 };
 
   /*
--- a/devtools/client/shared/devices.js
+++ b/devtools/client/shared/devices.js
@@ -1,17 +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";
 
 const { getJSON } = require("devtools/client/shared/getjson");
 const Services = require("Services");
-const promise = require("promise");
+const defer = require("devtools/shared/defer");
 
 const DEVICES_URL = "devtools.devices.url";
 const Strings = Services.strings.createBundle("chrome://devtools/locale/device.properties");
 
 /* This is a catalog of common web-enabled devices and their properties,
  * intended for (mobile) device emulation.
  *
  * The properties of a device are:
@@ -43,17 +43,17 @@ function AddDevice(device, type = "phone
     list = localDevices[type] = [];
   }
   list.push(device);
 }
 exports.AddDevice = AddDevice;
 
 // Get the complete devices catalog.
 function GetDevices() {
-  let deferred = promise.defer();
+  let deferred = defer();
 
   // Fetch common devices from Mozilla's CDN.
   getJSON(DEVICES_URL).then(devices => {
     for (let type in localDevices) {
       if (!devices[type]) {
         devices.TYPES.push(type);
         devices[type] = [];
       }
--- a/devtools/client/shared/doorhanger.js
+++ b/devtools/client/shared/doorhanger.js
@@ -3,17 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { Ci, Cc } = require("chrome");
 const Services = require("Services");
 const { DOMHelpers } = require("resource://devtools/client/shared/DOMHelpers.jsm");
 const { Task } = require("devtools/shared/task");
-const { Promise } = require("resource://gre/modules/Promise.jsm");
+const defer = require("devtools/shared/defer");
 const { getMostRecentBrowserWindow } = require("sdk/window/utils");
 
 const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 const DEV_EDITION_PROMO_URL = "chrome://devtools/content/framework/dev-edition-promo/dev-edition-promo.xul";
 const DEV_EDITION_PROMO_ENABLED_PREF = "devtools.devedition.promo.enabled";
 const DEV_EDITION_PROMO_SHOWN_PREF = "devtools.devedition.promo.shown";
 const DEV_EDITION_PROMO_URL_PREF = "devtools.devedition.promo.url";
 const LOCALE = Cc["@mozilla.org/chrome/chrome-registry;1"]
@@ -132,17 +132,17 @@ function setDoorhangerStyle(panel, frame
 
   frame.style.borderRadius = "5px";
   frame.setAttribute("flex", "1");
   frame.setAttribute("width", "450");
   frame.setAttribute("height", "179");
 }
 
 function onFrameLoad(frame) {
-  let { resolve, promise } = Promise.defer();
+  let { resolve, promise } = defer();
 
   if (frame.contentWindow) {
     let domHelper = new DOMHelpers(frame.contentWindow);
     domHelper.onceDOMReady(resolve);
   } else {
     let callback = () => {
       frame.removeEventListener("DOMContentLoaded", callback);
       resolve();
@@ -153,12 +153,12 @@ function onFrameLoad(frame) {
   return promise;
 }
 
 function getGBrowser() {
   return getMostRecentBrowserWindow().gBrowser;
 }
 
 function wait(n) {
-  let { resolve, promise } = Promise.defer();
+  let { resolve, promise } = defer();
   setTimeout(resolve, n);
   return promise;
 }
--- a/devtools/client/shared/frame-script-utils.js
+++ b/devtools/client/shared/frame-script-utils.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 */
 /* global addMessageListener, sendAsyncMessage, content */
 "use strict";
 var {classes: Cc, interfaces: Ci, utils: Cu} = Components;
 const {require, loader} = Cu.import("resource://devtools/shared/Loader.jsm", {});
-const promise = require("promise");
+const defer = require("devtools/shared/defer");
 const { Task } = require("devtools/shared/task");
 
 loader.lazyGetter(this, "nsIProfilerModule", () => {
   return Cc["@mozilla.org/tools/profiler;1"].getService(Ci.nsIProfiler);
 });
 
 addMessageListener("devtools:test:history", function ({ data }) {
   content.history[data.direction]();
@@ -56,17 +56,17 @@ function promiseXHR(data) {
   let method = data.method || "GET";
   let url = data.url || content.location.href;
   let body = data.body || "";
 
   if (data.nocache) {
     url += "?devtools-cachebust=" + Math.random();
   }
 
-  let deferred = promise.defer();
+  let deferred = defer();
   xhr.addEventListener("loadend", function loadend(event) {
     xhr.removeEventListener("loadend", loadend);
     deferred.resolve({ status: xhr.status, response: xhr.response });
   });
 
   xhr.open(method, url);
 
   // Set request headers
--- a/devtools/client/shared/getjson.js
+++ b/devtools/client/shared/getjson.js
@@ -1,16 +1,16 @@
 /* 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 {CC} = require("chrome");
-const promise = require("promise");
+const defer = require("devtools/shared/defer");
 const Services = require("Services");
 
 loader.lazyRequireGetter(this, "asyncStorage", "devtools/shared/async-storage");
 
 const XMLHttpRequest = CC("@mozilla.org/xmlextras/xmlhttprequest;1");
 
 /**
  * Downloads and caches a JSON file from an URL given by a pref.
@@ -19,17 +19,17 @@ const XMLHttpRequest = CC("@mozilla.org/
  *        The preference for the target URL
  *
  * @return {Promise}
  *         - Resolved with the JSON object in case of successful request
  *           or cache hit
  *         - Rejected with an error message in case of failure
  */
 exports.getJSON = function (prefName) {
-  let deferred = promise.defer();
+  let deferred = defer();
   let xhr = new XMLHttpRequest();
 
   // We used to store cached data in preferences, but now we use asyncStorage
   // Migration step: if it still exists, move this now useless preference in its
   // new location and clear it
   if (Services.prefs.prefHasUserValue(prefName + "_cache")) {
     let json = Services.prefs.getCharPref(prefName + "_cache");
     asyncStorage.setItem(prefName + "_cache", json).catch(function (e) {
--- a/devtools/client/shared/redux/middleware/promise.js
+++ b/devtools/client/shared/redux/middleware/promise.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 uuidgen = require("sdk/util/uuid").uuid;
-const promise = require("promise");
+const defer = require("devtools/shared/defer");
 const {
   entries, toObject, executeSoon
 } = require("devtools/shared/DevToolsUtils");
 const PROMISE = exports.PROMISE = "@@dispatch/promise";
 
 function promiseMiddleware({ dispatch, getState }) {
   return next => action => {
     if (!(PROMISE in action)) {
@@ -24,17 +24,17 @@ function promiseMiddleware({ dispatch, g
     action = Object.assign(
       toObject(entries(action).filter(pair => pair[0] !== PROMISE)), { seqId }
     );
 
     dispatch(Object.assign({}, action, { status: "start" }));
 
     // Return the promise so action creators can still compose if they
     // want to.
-    const deferred = promise.defer();
+    const deferred = defer();
     promiseInst.then(value => {
       executeSoon(() => {
         dispatch(Object.assign({}, action, {
           status: "done",
           value: value
         }));
         deferred.resolve(value);
       });
--- a/devtools/client/shared/redux/middleware/test/head.js
+++ b/devtools/client/shared/redux/middleware/test/head.js
@@ -1,21 +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/. */
 
 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 promise = require("promise");
+var defer = require("devtools/shared/defer");
 
 DevToolsUtils.testing = true;
 
 function waitUntilState(store, predicate) {
-  let deferred = promise.defer();
+  let deferred = defer();
   let unsubscribe = store.subscribe(check);
 
   function check() {
     if (predicate(store.getState())) {
       unsubscribe();
       deferred.resolve();
     }
   }
--- a/devtools/client/shared/test/browser_flame-graph-05.js
+++ b/devtools/client/shared/test/browser_flame-graph-05.js
@@ -73,17 +73,17 @@ function* testGraph(host, graph) {
   let distanceLeft = graph._selection.start;
   let distanceRight = TEST_BOUNDS.endTime * TEST_DPI_DENSITIY - graph._selection.end;
 
   ok(Math.abs(distanceRight - distanceLeft) < 0.1,
     "The graph zoomed correctly towards the center point.");
 }
 
 function pressKeyForTime(graph, keyCode, ms) {
-  let deferred = promise.defer();
+  let deferred = defer();
 
   graph._onKeyDown({ keyCode });
 
   setTimeout(() => {
     graph._onKeyUp({ keyCode });
     deferred.resolve();
   }, ms);
 
--- a/devtools/client/shared/test/browser_inplace-editor-01.js
+++ b/devtools/client/shared/test/browser_inplace-editor-01.js
@@ -41,17 +41,17 @@ function testMultipleInitialization(doc)
   is(doc.querySelectorAll("span").length, 2,
     "Correct number of <span> elements");
   is(doc.querySelectorAll("span.autosizer").length, 1,
     "There is an autosizer element");
 }
 
 function testReturnCommit(doc) {
   info("Testing that pressing return commits the new value");
-  let def = promise.defer();
+  let def = defer();
 
   createInplaceEditorAndClick({
     initial: "explicit initial",
     start: function (editor) {
       is(editor.input.value, "explicit initial",
         "Explicit initial value should be used.");
       editor.input.value = "Test Value";
       EventUtils.sendKey("return");
@@ -59,48 +59,48 @@ function testReturnCommit(doc) {
     done: onDone("Test Value", true, def)
   }, doc);
 
   return def.promise;
 }
 
 function testBlurCommit(doc) {
   info("Testing that bluring the field commits the new value");
-  let def = promise.defer();
+  let def = defer();
 
   createInplaceEditorAndClick({
     start: function (editor) {
       is(editor.input.value, "Edit Me!", "textContent of the span used.");
       editor.input.value = "Test Value";
       editor.input.blur();
     },
     done: onDone("Test Value", true, def)
   }, doc, "Edit Me!");
 
   return def.promise;
 }
 
 function testAdvanceCharCommit(doc) {
   info("Testing that configured advanceChars commit the new value");
-  let def = promise.defer();
+  let def = defer();
 
   createInplaceEditorAndClick({
     advanceChars: ":",
     start: function (editor) {
       EventUtils.sendString("Test:");
     },
     done: onDone("Test", true, def)
   }, doc);
 
   return def.promise;
 }
 
 function testAdvanceCharsFunction(doc) {
   info("Testing advanceChars as a function");
-  let def = promise.defer();
+  let def = defer();
 
   let firstTime = true;
 
   createInplaceEditorAndClick({
     initial: "",
     advanceChars: function (charCode, text, insertionPoint) {
       if (charCode !== Components.interfaces.nsIDOMKeyEvent.DOM_VK_COLON) {
         return false;
@@ -121,17 +121,17 @@ function testAdvanceCharsFunction(doc) {
     done: onDone(":Test", true, def)
   }, doc);
 
   return def.promise;
 }
 
 function testEscapeCancel(doc) {
   info("Testing that escape cancels the new value");
-  let def = promise.defer();
+  let def = defer();
 
   createInplaceEditorAndClick({
     initial: "initial text",
     start: function (editor) {
       editor.input.value = "Test Value";
       EventUtils.sendKey("escape");
     },
     done: onDone("initial text", false, def)
--- a/devtools/client/shared/test/browser_inplace-editor-02.js
+++ b/devtools/client/shared/test/browser_inplace-editor-02.js
@@ -17,17 +17,17 @@ add_task(function* () {
   yield testTrimmed(doc);
 
   host.destroy();
   gBrowser.removeCurrentTab();
 });
 
 function testNonTrimmed(doc) {
   info("Testing the trimOutput=false option");
-  let def = promise.defer();
+  let def = defer();
 
   let initial = "\nMultiple\nLines\n";
   let changed = " \nMultiple\nLines\n with more whitespace ";
   createInplaceEditorAndClick({
     trimOutput: false,
     multiline: true,
     initial: initial,
     start: function (editor) {
@@ -38,17 +38,17 @@ function testNonTrimmed(doc) {
     done: onDone(changed, true, def)
   }, doc);
 
   return def.promise;
 }
 
 function testTrimmed(doc) {
   info("Testing the trimOutput=true option (default value)");
-  let def = promise.defer();
+  let def = defer();
 
   let initial = "\nMultiple\nLines\n";
   let changed = " \nMultiple\nLines\n with more whitespace ";
   createInplaceEditorAndClick({
     initial: initial,
     multiline: true,
     start: function (editor) {
       is(editor.input.value, initial, "Explicit initial value should be used.");
--- a/devtools/client/shared/test/browser_mdn-docs-01.js
+++ b/devtools/client/shared/test/browser_mdn-docs-01.js
@@ -143,17 +143,17 @@ function checkLinkClick(link) {
   function newTabListener(e) {
     gBrowser.tabContainer.removeEventListener("TabOpen", newTabListener);
     var tab = e.target;
     BrowserTestUtils.browserLoaded(tab.linkedBrowser, false,
                                    url => { return url != "about:blank"; })
       .then(url => loadListener(tab));
   }
 
-  let deferred = promise.defer();
+  let deferred = defer();
   info("Check that clicking the link opens a new tab with the correct URI");
   gBrowser.tabContainer.addEventListener("TabOpen", newTabListener, false);
   info("Click the link to MDN");
   link.click();
   return deferred.promise;
 }
 
 /**
--- a/devtools/client/shared/test/browser_treeWidget_keyboard_interaction.js
+++ b/devtools/client/shared/test/browser_treeWidget_keyboard_interaction.js
@@ -3,17 +3,16 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests that keyboard interaction works fine with the tree widget
 
 const TEST_URI = "data:text/html;charset=utf-8,<head>" +
   "<link rel='stylesheet' type='text/css' href='chrome://devtools/skin/widg" +
   "ets.css'></head><body><div></div><span></span></body>";
 const {TreeWidget} = require("devtools/client/shared/widgets/TreeWidget");
-const Promise = require("promise");
 
 add_task(function* () {
   yield addTab("about:blank");
   let [host, win, doc] = yield createHost("bottom", TEST_URI);
 
   let tree = new TreeWidget(doc.querySelector("div"), {
     defaultType: "store"
   });
@@ -84,141 +83,141 @@ function click(node) {
  */
 function* testKeyboardInteraction(tree, win) {
   info("Testing keyboard interaction with the tree");
   let event;
   let pass = (e, d, a) => event.resolve([e, d, a]);
 
   info("clicking on first top level item");
   let node = tree.root.children.firstChild.firstChild;
-  event = Promise.defer();
+  event = defer();
   tree.once("select", pass);
   click(node);
   yield event.promise;
   node = tree.root.children.firstChild.nextSibling.firstChild;
   // node should not have selected class
   ok(!node.classList.contains("theme-selected"), "Node should not have selected class");
   ok(!node.hasAttribute("expanded"), "Node is not expanded");
 
   info("Pressing down key to select next item");
-  event = Promise.defer();
+  event = defer();
   tree.once("select", pass);
   EventUtils.sendKey("DOWN", win);
   let [name, data, attachment] = yield event.promise;
   is(name, "select", "Select event was fired after pressing down");
   is(data[0], "level1", "Correct item was selected after pressing down");
   ok(!attachment, "null attachment was emitted");
   ok(node.classList.contains("theme-selected"), "Node has selected class");
   ok(node.hasAttribute("expanded"), "Node is expanded now");
 
   info("Pressing down key again to select next item");
-  event = Promise.defer();
+  event = defer();
   tree.once("select", pass);
   EventUtils.sendKey("DOWN", win);
   [name, data, attachment] = yield event.promise;
   is(data.length, 2, "Correct level item was selected after second down keypress");
   is(data[0], "level1", "Correct parent level");
   is(data[1], "level2", "Correct second level");
 
   info("Pressing down key again to select next item");
-  event = Promise.defer();
+  event = defer();
   tree.once("select", pass);
   EventUtils.sendKey("DOWN", win);
   [name, data, attachment] = yield event.promise;
   is(data.length, 3, "Correct level item was selected after third down keypress");
   is(data[0], "level1", "Correct parent level");
   is(data[1], "level2", "Correct second level");
   is(data[2], "level3", "Correct third level");
 
   info("Pressing down key again to select next item");
-  event = Promise.defer();
+  event = defer();
   tree.once("select", pass);
   EventUtils.sendKey("DOWN", win);
   [name, data, attachment] = yield event.promise;
   is(data.length, 2, "Correct level item was selected after fourth down keypress");
   is(data[0], "level1", "Correct parent level");
   is(data[1], "level2-1", "Correct second level");
 
   // pressing left to check expand collapse feature.
   // This does not emit any event, so listening for keypress
   tree.root.children.addEventListener("keypress", function onClick() {
     tree.root.children.removeEventListener("keypress", onClick);
     // executeSoon so that other listeners on the same method are executed first
     executeSoon(() => event.resolve(null));
   });
   info("Pressing left key to collapse the item");
-  event = Promise.defer();
+  event = defer();
   node = tree._selectedLabel;
   ok(node.hasAttribute("expanded"), "Item is expanded before left keypress");
   EventUtils.sendKey("LEFT", win);
   yield event.promise;
 
   ok(!node.hasAttribute("expanded"), "Item is not expanded after left keypress");
 
   // pressing left on collapsed item should select the previous item
 
   info("Pressing left key on collapsed item to select previous");
   tree.once("select", pass);
-  event = Promise.defer();
+  event = defer();
   // parent node should have no effect of this keypress
   node = tree.root.children.firstChild.nextSibling.firstChild;
   ok(node.hasAttribute("expanded"), "Parent is expanded");
   EventUtils.sendKey("LEFT", win);
   [name, data] = yield event.promise;
   is(data.length, 3, "Correct level item was selected after second left keypress");
   is(data[0], "level1", "Correct parent level");
   is(data[1], "level2", "Correct second level");
   is(data[2], "level3", "Correct third level");
   ok(node.hasAttribute("expanded"), "Parent is still expanded after left keypress");
 
   // pressing down again
 
   info("Pressing down key to select next item");
-  event = Promise.defer();
+  event = defer();
   tree.once("select", pass);
   EventUtils.sendKey("DOWN", win);
   [name, data, attachment] = yield event.promise;
   is(data.length, 2, "Correct level item was selected after fifth down keypress");
   is(data[0], "level1", "Correct parent level");
   is(data[1], "level2-1", "Correct second level");
 
   // collapsing the item to check expand feature.
 
   tree.root.children.addEventListener("keypress", function onClick() {
     tree.root.children.removeEventListener("keypress", onClick);
     executeSoon(() => event.resolve(null));
   });
   info("Pressing left key to collapse the item");
-  event = Promise.defer();
+  event = defer();
   node = tree._selectedLabel;
   ok(node.hasAttribute("expanded"), "Item is expanded before left keypress");
   EventUtils.sendKey("LEFT", win);
   yield event.promise;
   ok(!node.hasAttribute("expanded"), "Item is collapsed after left keypress");
 
   // pressing right should expand this now.
 
   tree.root.children.addEventListener("keypress", function onClick() {
     tree.root.children.removeEventListener("keypress", onClick);
     executeSoon(() => event.resolve(null));
   });
   info("Pressing right key to expend the collapsed item");
-  event = Promise.defer();
+  event = defer();
   node = tree._selectedLabel;
   ok(!node.hasAttribute("expanded"), "Item is collapsed before right keypress");
   EventUtils.sendKey("RIGHT", win);
   yield event.promise;
   ok(node.hasAttribute("expanded"), "Item is expanded after right keypress");
 
   // selecting last item node to test edge navigation case
 
   tree.selectedItem = ["level1.1", "level2", "level3"];
   node = tree._selectedLabel;
   // pressing down again should not change selection
-  event = Promise.defer();
+  event = defer();
   tree.root.children.addEventListener("keypress", function onClick() {
     tree.root.children.removeEventListener("keypress", onClick);
     executeSoon(() => event.resolve(null));
   });
   info("Pressing down key on last item of the tree");
   EventUtils.sendKey("DOWN", win);
   yield event.promise;
 
--- a/devtools/client/shared/test/browser_treeWidget_mouse_interaction.js
+++ b/devtools/client/shared/test/browser_treeWidget_mouse_interaction.js
@@ -3,17 +3,16 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests that mouse interaction works fine with tree widget
 
 const TEST_URI = "data:text/html;charset=utf-8,<head>" +
   "<link rel='stylesheet' type='text/css' href='chrome://devtools/skin/widg" +
   "ets.css'></head><body><div></div><span></span></body>";
 const {TreeWidget} = require("devtools/client/shared/widgets/TreeWidget");
-const Promise = require("promise");
 
 add_task(function* () {
   yield addTab("about:blank");
   let [host, win, doc] = yield createHost("bottom", TEST_URI);
 
   let tree = new TreeWidget(doc.querySelector("div"), {
     defaultType: "store"
   });
@@ -87,30 +86,30 @@ function* testMouseInteraction(tree) {
   let event;
   let pass = (e, d, a) => event.resolve([e, d, a]);
 
   ok(!tree.selectedItem, "Nothing should be selected beforehand");
 
   tree.once("select", pass);
   let node = tree.root.children.firstChild.firstChild;
   info("clicking on first top level item");
-  event = Promise.defer();
+  event = defer();
   ok(!node.classList.contains("theme-selected"),
      "Node should not have selected class before clicking");
   click(node);
   let [name, data, attachment] = yield event.promise;
   ok(node.classList.contains("theme-selected"),
      "Node has selected class after click");
   is(data[0], "level1.2", "Correct tree path is emitted");
   ok(attachment && attachment.foo, "Correct attachment is emitted");
   is(attachment.foo, "bar", "Correct attachment value is emitted");
 
   info("clicking second top level item with children to check if it expands");
   let node2 = tree.root.children.firstChild.nextSibling.firstChild;
-  event = Promise.defer();
+  event = defer();
   // node should not have selected class
   ok(!node2.classList.contains("theme-selected"),
      "New node should not have selected class before clicking");
   ok(!node2.hasAttribute("expanded"), "New node is not expanded before clicking");
   tree.once("select", pass);
   click(node2);
   [name, data, attachment] = yield event.promise;
   ok(node2.classList.contains("theme-selected"),
@@ -120,17 +119,17 @@ function* testMouseInteraction(tree) {
   ok(node2.hasAttribute("expanded"), "New node expanded after click");
 
   ok(!node.classList.contains("theme-selected"),
      "Old node should not have selected class after the click on new node");
 
 
   // clicking again should just collapse
   // this will not emit "select" event
-  event = Promise.defer();
+  event = defer();
   node2.addEventListener("click", function onClick() {
     node2.removeEventListener("click", onClick);
     executeSoon(() => event.resolve(null));
   });
   click(node2);
   yield event.promise;
   ok(!node2.hasAttribute("expanded"), "New node collapsed after click again");
 }
--- a/devtools/client/shared/test/head.js
+++ b/devtools/client/shared/test/head.js
@@ -6,17 +6,16 @@
 
 "use strict";
 
 // shared-head.js handles imports, constants, and utility functions
 Services.scriptloader.loadSubScript("chrome://mochitests/content/browser/devtools/client/framework/test/shared-head.js", this);
 
 const {DOMHelpers} = Cu.import("resource://devtools/client/shared/DOMHelpers.jsm", {});
 const {Hosts} = require("devtools/client/framework/toolbox-hosts");
-const {defer} = require("promise");
 
 const TEST_URI_ROOT = "http://example.com/browser/devtools/client/shared/test/";
 const OPTIONS_VIEW_URL = TEST_URI_ROOT + "doc_options-view.xul";
 
 function catchFail(func) {
   return function () {
     try {
       return func.apply(null, arguments);
--- a/devtools/client/shared/test/test-actor-registry.js
+++ b/devtools/client/shared/test/test-actor-registry.js
@@ -9,25 +9,26 @@
   var Ci = Components.interfaces;
   var Cc = Components.classes;
   var CC = Components.Constructor;
 
   var { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
   var { NetUtil } = Cu.import("resource://gre/modules/NetUtil.jsm", {});
   var { fetch } = require("devtools/shared/DevToolsUtils");
   var promise = require("promise");
+  var defer = require("devtools/shared/defer");
 
   var TEST_URL_ROOT = "http://example.com/browser/devtools/client/shared/test/";
   var ACTOR_URL = TEST_URL_ROOT + "test-actor.js";
 
 // Register a test actor that can operate on the remote document
   exports.registerTestActor = Task.async(function* (client) {
   // First, instanciate ActorRegistryFront to be able to dynamically
   // register an actor
-    let deferred = promise.defer();
+    let deferred = defer();
     client.listTabs(deferred.resolve);
     let response = yield deferred.promise;
     let { ActorRegistryFront } = require("devtools/shared/fronts/actor-registry");
     let registryFront = ActorRegistryFront(client, response);
 
   // Then ask to register our test-actor to retrieve its front
     let options = {
       type: { tab: true },
--- a/devtools/client/shared/test/test-actor.js
+++ b/devtools/client/shared/test/test-actor.js
@@ -3,17 +3,17 @@
  http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 // A helper actor for inspector and markupview tests.
 
 var { Cc, Ci, Cu, Cr } = require("chrome");
 const {getRect, getElementFromPoint, getAdjustedQuads} = require("devtools/shared/layout/utils");
-const promise = require("promise");
+const defer = require("devtools/shared/defer");
 const {Task} = require("devtools/shared/task");
 var DOMUtils = Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
 var loader = Cc["@mozilla.org/moz/jssubscript-loader;1"]
             .getService(Ci.mozIJSSubScriptLoader);
 
 // Set up a dummy environment so that EventUtils works. We need to be careful to
 // pass a window object into each EventUtils method we call rather than having
 // it rely on the |window| global.
@@ -182,17 +182,17 @@ var TestActor = exports.TestActor = prot
    * Subscribe to the box-model highlighter's update event, modify an attribute of
    * the currently highlighted node and send a message when the highlighter has
    * updated.
    * @param {String} the name of the attribute to be changed
    * @param {String} the new value for the attribute
    * @param {String} actorID The highlighter actor ID
    */
   changeHighlightedNodeWaitForUpdate: protocol.method(function (name, value, actorID) {
-    let deferred = promise.defer();
+    let deferred = defer();
 
     let highlighter = this.conn.getActor(actorID);
     let {_highlighter: h} = highlighter;
 
     h.once("updated", () => {
       deferred.resolve();
     });
 
@@ -252,17 +252,17 @@ var TestActor = exports.TestActor = prot
    * Change the zoom level of the page.
    * Optionally subscribe to the box-model highlighter's update event and waiting
    * for it to refresh before responding.
    * @param {Number} level The new zoom level
    * @param {String} actorID Optional. The highlighter actor ID
    */
   changeZoomLevel: protocol.method(function (level, actorID) {
     dumpn("Zooming page to " + level);
-    let deferred = promise.defer();
+    let deferred = defer();
 
     if (actorID) {
       let actor = this.conn.getActor(actorID);
       let {_highlighter: h} = actor;
       h.once("updated", () => {
         deferred.resolve();
       });
     } else {
@@ -381,17 +381,17 @@ var TestActor = exports.TestActor = prot
       pseudo: Arg(1, "string")
     },
     response: {
       value: RetVal("boolean")
     }
   }),
 
   loadAndWaitForCustomEvent: protocol.method(function (url) {
-    let deferred = promise.defer();
+    let deferred = defer();
     let self = this;
     // Wait for DOMWindowCreated first, as listening on the current outerwindow
     // doesn't allow receiving test-page-processing-done.
     this.tabActor.chromeEventHandler.addEventListener("DOMWindowCreated", function onWindowCreated() {
       self.tabActor.chromeEventHandler.removeEventListener("DOMWindowCreated", onWindowCreated);
       self.content.addEventListener("test-page-processing-done", function onEvent() {
         self.content.removeEventListener("test-page-processing-done", onEvent);
         deferred.resolve();
@@ -543,17 +543,17 @@ var TestActor = exports.TestActor = prot
 
   /**
    * Reload an iframe and wait for its load event.
    * @param {String} selector The node selector
    */
   reloadFrame: protocol.method(function (selector) {
     let node = this._querySelector(selector);
 
-    let deferred = promise.defer();
+    let deferred = defer();
 
     let onLoad = function () {
       node.removeEventListener("load", onLoad);
       deferred.resolve();
     };
     node.addEventListener("load", onLoad);
 
     node.contentWindow.location.reload();
@@ -594,17 +594,17 @@ var TestActor = exports.TestActor = prot
    * @return {Object} An object with x / y properties, representing the number
    * of pixels that the document has been scrolled horizontally and vertically.
    */
   scrollWindow: protocol.method(function (x, y, relative) {
     if (isNaN(x) || isNaN(y)) {
       return {};
     }
 
-    let deferred = promise.defer();
+    let deferred = defer();
     this.content.addEventListener("scroll", function onScroll(event) {
       this.removeEventListener("scroll", onScroll);
 
       let data = {x: this.content.scrollX, y: this.content.scrollY};
       deferred.resolve(data);
     });
 
     this.content[relative ? "scrollBy" : "scrollTo"](x, y);
@@ -620,17 +620,17 @@ var TestActor = exports.TestActor = prot
       value: RetVal("json")
     }
   }),
 
   /**
    * Forces the reflow and waits for the next repaint.
    */
   reflow: protocol.method(function () {
-    let deferred = promise.defer();
+    let deferred = defer();
     this.content.document.documentElement.offsetWidth;
     this.content.requestAnimationFrame(deferred.resolve);
 
     return deferred.promise;
   }),
 
   getNodeRect: protocol.method(Task.async(function* (selector) {
     let node = this._querySelector(selector);
--- a/devtools/client/shared/widgets/FlameGraph.js
+++ b/devtools/client/shared/widgets/FlameGraph.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/. */
 "use strict";
 
 const { Task } = require("devtools/shared/task");
 const { ViewHelpers, setNamedTimeout } = require("devtools/client/shared/widgets/view-helpers");
 const { LocalizationHelper } = require("devtools/client/shared/l10n");
 
-loader.lazyRequireGetter(this, "promise");
+loader.lazyRequireGetter(this, "defer", "devtools/shared/defer");
 loader.lazyRequireGetter(this, "EventEmitter",
   "devtools/shared/event-emitter");
 
 loader.lazyRequireGetter(this, "getColor",
   "devtools/client/shared/theme", true);
 
 loader.lazyRequireGetter(this, "CATEGORY_MAPPINGS",
   "devtools/client/performance/modules/categories", true);
@@ -139,17 +139,17 @@ const COLOR_PALLETTE = Array.from(Array(
  *        The parent node holding the graph.
  * @param number sharpness [optional]
  *        Defaults to the current device pixel ratio.
  */
 function FlameGraph(parent, sharpness) {
   EventEmitter.decorate(this);
 
   this._parent = parent;
-  this._ready = promise.defer();
+  this._ready = defer();
 
   this.setTheme();
 
   AbstractCanvasGraph.createIframe(GRAPH_SRC, parent, iframe => {
     this._iframe = iframe;
     this._window = iframe.contentWindow;
     this._document = iframe.contentDocument;
     this._pixelRatio = sharpness || this._window.devicePixelRatio;
--- a/devtools/client/shared/widgets/Graphs.js
+++ b/devtools/client/shared/widgets/Graphs.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/. */
 "use strict";
 
 const { Task } = require("devtools/shared/task");
 const { setNamedTimeout } = require("devtools/client/shared/widgets/view-helpers");
 const { getCurrentZoom } = require("devtools/shared/layout/utils");
 
-loader.lazyRequireGetter(this, "promise");
+loader.lazyRequireGetter(this, "defer", "devtools/shared/defer");
 loader.lazyRequireGetter(this, "EventEmitter",
   "devtools/shared/event-emitter");
 
 loader.lazyImporter(this, "DevToolsWorker",
   "resource://devtools/shared/worker/worker.js");
 
 const HTML_NS = "http://www.w3.org/1999/xhtml";
 const GRAPH_SRC = "chrome://devtools/content/shared/widgets/graphs-frame.xhtml";
@@ -95,17 +95,17 @@ this.GraphAreaResizer = function () {
  *        Currently supported: "line-graph" only.
  * @param number sharpness [optional]
  *        Defaults to the current device pixel ratio.
  */
 this.AbstractCanvasGraph = function (parent, name, sharpness) {
   EventEmitter.decorate(this);
 
   this._parent = parent;
-  this._ready = promise.defer();
+  this._ready = defer();
 
   this._uid = "canvas-graph-" + Date.now();
   this._renderTargets = new Map();
 
   AbstractCanvasGraph.createIframe(GRAPH_SRC, parent, iframe => {
     this._iframe = iframe;
     this._window = iframe.contentWindow;
     this._topWindow = this._window.top;
--- a/devtools/client/shared/widgets/MdnDocsWidget.js
+++ b/devtools/client/shared/widgets/MdnDocsWidget.js
@@ -20,17 +20,17 @@
  * - the MdnDocsWidget class, that manages and updates a tooltip
  * document whose content is taken from MDN. If you want to embed
  * the content in a tooltip, use this in conjunction with Tooltip.js.
  */
 
 "use strict";
 
 const Services = require("Services");
-const Promise = require("promise");
+const defer = require("devtools/shared/defer");
 const {getCSSLexer} = require("devtools/shared/css-lexer");
 
 // Parameters for the XHR request
 // see https://developer.mozilla.org/en-US/docs/MDN/Kuma/API#Document_parameters
 const XHR_PARAMS = "?raw&macros";
 // URL for the XHR request
 var XHR_CSS_URL = "https://developer.mozilla.org/en-US/docs/Web/CSS/";
 
@@ -150,17 +150,17 @@ exports.appendSyntaxHighlightedCSS = app
  *
  * @return {promise}
  * The promise is resolved with the page as an XML document.
  *
  * The promise is rejected with an error message if
  * we could not load the page.
  */
 function getMdnPage(pageUrl) {
-  let deferred = Promise.defer();
+  let deferred = defer();
 
   let xhr = new XMLHttpRequest();
 
   xhr.addEventListener("load", onLoaded, false);
   xhr.addEventListener("error", onError, false);
 
   xhr.open("GET", pageUrl);
   xhr.responseType = "document";
@@ -193,17 +193,17 @@ function getMdnPage(pageUrl) {
  * The promise is resolved with an object containing:
  * - summary: a short summary of the property
  * - syntax: some example syntax
  *
  * The promise is rejected with an error message if
  * we could not load the page.
  */
 function getCssDocs(cssProperty) {
-  let deferred = Promise.defer();
+  let deferred = defer();
   let pageUrl = XHR_CSS_URL + cssProperty + XHR_PARAMS;
 
   getMdnPage(pageUrl).then(parseDocsFromResponse, handleRejection);
 
   function parseDocsFromResponse(responseDocument) {
     let theDocs = {};
     theDocs.summary = getSummary(responseDocument);
     theDocs.syntax = getSyntax(responseDocument);
@@ -343,17 +343,17 @@ MdnDocsWidget.prototype = {
       // hide the throbber
       elements.info.classList.remove("devtools-throbber");
 
       // although gotError is called when there's an error, we have handled
       // the error, so call resolve not reject.
       deferred.resolve(this);
     }
 
-    let deferred = Promise.defer();
+    let deferred = defer();
     let elements = this.elements;
 
     initializeDocument(propertyName);
     getCssDocs(propertyName).then(finalizeDocument, gotError);
 
     return deferred.promise;
   },
 
--- a/devtools/client/shared/widgets/Tooltip.js
+++ b/devtools/client/shared/widgets/Tooltip.js
@@ -1,16 +1,16 @@
 /* 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 {Ci} = require("chrome");
-const promise = require("promise");
+const defer = require("devtools/shared/defer");
 const {Spectrum} = require("devtools/client/shared/widgets/Spectrum");
 const {CubicBezierWidget} =
       require("devtools/client/shared/widgets/CubicBezierWidget");
 const {MdnDocsWidget} = require("devtools/client/shared/widgets/MdnDocsWidget");
 const {CSSFilterEditorWidget} = require("devtools/client/shared/widgets/FilterWidget");
 const {TooltipToggle} = require("devtools/client/shared/widgets/tooltip/TooltipToggle");
 const EventEmitter = require("devtools/shared/event-emitter");
 const {colorUtils} = require("devtools/client/shared/css-color");
@@ -524,17 +524,17 @@ Tooltip.prototype = {
    * This function creates an iframe, loads the specified document
    * into it, sets the tooltip's content to the iframe, and returns
    * a promise.
    *
    * When the document is loaded, the function gets the content window
    * and resolves the promise with the content window.
    */
   setIFrameContent: function ({width, height}, url) {
-    let def = promise.defer();
+    let def = defer();
 
     // Create an iframe
     let iframe = this.doc.createElementNS(XHTML_NS, "iframe");
     iframe.setAttribute("transparent", true);
     iframe.setAttribute("width", width);
     iframe.setAttribute("height", height);
     iframe.setAttribute("flex", "1");
     iframe.setAttribute("tooltip", "aHTMLTooltip");
@@ -563,17 +563,17 @@ Tooltip.prototype = {
    */
   setColorPickerContent: function (color) {
     let dimensions = {width: "210", height: "216"};
     let panel = this.panel;
     return this.setIFrameContent(dimensions, SPECTRUM_FRAME).then(onLoaded);
 
     function onLoaded(iframe) {
       let win = iframe.contentWindow.wrappedJSObject;
-      let def = promise.defer();
+      let def = defer();
       let container = win.document.getElementById("spectrum");
       let spectrum = new Spectrum(container, color);
 
       function finalizeSpectrum() {
         spectrum.show();
         def.resolve(spectrum);
       }
 
@@ -597,17 +597,17 @@ Tooltip.prototype = {
    */
   setCubicBezierContent: function (bezier) {
     let dimensions = {width: "500", height: "360"};
     let panel = this.panel;
     return this.setIFrameContent(dimensions, CUBIC_BEZIER_FRAME).then(onLoaded);
 
     function onLoaded(iframe) {
       let win = iframe.contentWindow.wrappedJSObject;
-      let def = promise.defer();
+      let def = defer();
       let container = win.document.getElementById("container");
       let widget = new CubicBezierWidget(container, bezier);
 
       // Resolve to the widget instance whenever the popup becomes visible
       if (panel.state == "open") {
         def.resolve(widget);
       } else {
         panel.addEventListener("popupshown", function shown() {
@@ -627,17 +627,17 @@ Tooltip.prototype = {
   setFilterContent: function (filter) {
     let dimensions = {width: "500", height: "200"};
     let panel = this.panel;
 
     return this.setIFrameContent(dimensions, FILTER_FRAME).then(onLoaded);
 
     function onLoaded(iframe) {
       let win = iframe.contentWindow.wrappedJSObject;
-      let def = promise.defer();
+      let def = defer();
       let container = win.document.getElementById("container");
       let widget = new CSSFilterEditorWidget(container, filter);
 
       // Resolve to the widget instance whenever the popup becomes visible
       if (panel.state === "open") {
         def.resolve(widget);
       } else {
         panel.addEventListener("popupshown", function shown() {
--- a/devtools/client/shared/widgets/VariablesView.jsm
+++ b/devtools/client/shared/widgets/VariablesView.jsm
@@ -18,16 +18,17 @@ const ITEM_FLASH_DURATION = 300; // ms
 
 const { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
 const {XPCOMUtils} = require("resource://gre/modules/XPCOMUtils.jsm");
 const EventEmitter = require("devtools/shared/event-emitter");
 const DevToolsUtils = require("devtools/shared/DevToolsUtils");
 const Services = require("Services");
 const { getSourceNames } = require("devtools/client/shared/source-utils");
 const promise = require("promise");
+const defer = require("devtools/shared/defer");
 const { Heritage, ViewHelpers, setNamedTimeout } =
   require("devtools/client/shared/widgets/view-helpers");
 const { Task } = require("devtools/shared/task");
 const nodeConstants = require("devtools/shared/dom-node-constants");
 
 XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
   "resource://gre/modules/PluralForm.jsm");
 
@@ -2796,17 +2797,17 @@ Variable.prototype = Heritage.extend(Sco
       let nodeFront = this._nodeFront;
       if (!nodeFront) {
         nodeFront = yield this.toolbox.walker.getNodeActorFromObjectActor(this._valueGrip.actor);
       }
 
       if (nodeFront) {
         yield this.toolbox.selectTool("inspector");
 
-        let inspectorReady = promise.defer();
+        let inspectorReady = defer();
         this.toolbox.getPanel("inspector").once("inspector-updated", inspectorReady.resolve);
         yield this.toolbox.selection.setNodeFront(nodeFront, "variables-view");
         yield inspectorReady.promise;
       }
     }.bind(this));
   },
 
   /**
--- a/devtools/client/shared/widgets/VariablesViewController.jsm
+++ b/devtools/client/shared/widgets/VariablesViewController.jsm
@@ -7,16 +7,17 @@
 
 const { utils: Cu } = Components;
 
 var {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
 var {XPCOMUtils} = require("resource://gre/modules/XPCOMUtils.jsm");
 var {VariablesView} = require("resource://devtools/client/shared/widgets/VariablesView.jsm");
 var Services = require("Services");
 var promise = require("promise");
+var defer = require("devtools/shared/defer");
 var {LocalizationHelper} = require("devtools/client/shared/l10n");
 
 Object.defineProperty(this, "WebConsoleUtils", {
   get: function () {
     return require("devtools/shared/webconsole/utils").Utils;
   },
   configurable: true,
   enumerable: true
@@ -137,17 +138,17 @@ VariablesViewController.prototype = {
    * @param Variable aTarget
    *        The target Variable/Property to put the retrieved string into.
    * @param LongStringActor aGrip
    *        The long string grip that use to retrieve the full string.
    * @return Promise
    *         The promise that will be resolved when the string is retrieved.
    */
   _populateFromLongString: function (aTarget, aGrip) {
-    let deferred = promise.defer();
+    let deferred = defer();
 
     let from = aGrip.initial.length;
     let to = Math.min(aGrip.length, MAX_LONG_STRING_LENGTH);
 
     this._getLongStringClient(aGrip).substring(from, to, aResponse => {
       // Stop tracking the actor because it's no longer needed.
       this.releaseActor(aGrip);
 
@@ -188,17 +189,17 @@ VariablesViewController.prototype = {
       let sliceGrip = {
         type: "property-iterator",
         propertyIterator: iterator,
         start: start,
         count: count
       };
 
       // Query the name of the first and last items for this slice
-      let deferred = promise.defer();
+      let deferred = defer();
       iterator.names([start, start + count - 1], ({ names }) => {
         let label = "[" + names[0] + L10N.ellipsis + names[1] + "]";
         let item = aTarget.addItem(label, {}, { internalItem: true });
         item.showArrow();
         this.addExpander(item, sliceGrip);
         deferred.resolve();
       });
       promises.push(deferred.promise);
@@ -217,17 +218,17 @@ VariablesViewController.prototype = {
    *        The property iterator grip.
    */
   _populateFromPropertyIterator: function (aTarget, aGrip) {
     if (aGrip.count >= MAX_PROPERTY_ITEMS) {
       // We already started to split, but there is still too many properties, split again.
       return this._populatePropertySlices(aTarget, aGrip);
     }
     // We started slicing properties, and the slice is now small enough to be displayed
-    let deferred = promise.defer();
+    let deferred = defer();
     aGrip.propertyIterator.slice(aGrip.start, aGrip.count,
       ({ ownProperties }) => {
         // Add all the variable properties.
         if (Object.keys(ownProperties).length > 0) {
           aTarget.addItems(ownProperties, {
             sorted: true,
             // Expansion handlers must be set after the properties are added.
             callback: this.addExpander
@@ -247,17 +248,17 @@ VariablesViewController.prototype = {
    * @param object aGrip
    *        The grip to use to populate the target.
    * @param string aQuery [optional]
    *        The query string used to fetch only a subset of properties
    */
   _populateFromObjectWithIterator: function (aTarget, aGrip, aQuery) {
     // FF40+ starts exposing `ownPropertyLength` on ObjectActor's grip,
     // as well as `enumProperties` request.
-    let deferred = promise.defer();
+    let deferred = defer();
     let objectClient = this._getObjectClient(aGrip);
     let isArray = aGrip.preview && aGrip.preview.kind === "ArrayLike";
     if (isArray) {
       // First enumerate array items, e.g. properties from `0` to `array.length`.
       let options = {
         ignoreNonIndexedProperties: true,
         query: aQuery
       };
@@ -349,31 +350,31 @@ VariablesViewController.prototype = {
         obj: aGrip
       });
     }
 
     // Fetch properties by slices if there is too many in order to prevent UI freeze.
     if ("ownPropertyLength" in aGrip && aGrip.ownPropertyLength >= MAX_PROPERTY_ITEMS) {
       return this._populateFromObjectWithIterator(aTarget, aGrip)
                  .then(() => {
-                   let deferred = promise.defer();
+                   let deferred = defer();
                    let objectClient = this._getObjectClient(aGrip);
                    objectClient.getPrototype(({ prototype }) => {
                      this._populateObjectPrototype(aTarget, prototype);
                      deferred.resolve();
                    });
                    return deferred.promise;
                  });
     }
 
     return this._populateProperties(aTarget, aGrip);
   },
 
   _populateProperties: function (aTarget, aGrip, aOptions) {
-    let deferred = promise.defer();
+    let deferred = defer();
 
     let objectClient = this._getObjectClient(aGrip);
     objectClient.getPrototypeAndProperties(aResponse => {
       let ownProperties = aResponse.ownProperties || {};
       let prototype = aResponse.prototype || null;
       // 'safeGetterValues' is new and isn't necessary defined on old actors.
       let safeGetterValues = aResponse.safeGetterValues || {};
       let sortable = VariablesView.isSortable(aGrip.class);
@@ -445,17 +446,17 @@ VariablesViewController.prototype = {
       let closure = funcScope.addItem(label, undefined, {relaxed: true});
       closure.target.setAttribute("scope", "");
       closure.showArrow();
 
       // Add nodes for every argument and every other variable in scope.
       if (environment.bindings) {
         this._populateWithEnvironmentBindings(closure, environment.bindings);
       } else {
-        let deferred = promise.defer();
+        let deferred = defer();
         objectScopes.push(deferred.promise);
         this._getEnvironmentClient(environment).getBindings(response => {
           this._populateWithEnvironmentBindings(closure, response.bindings);
           deferred.resolve();
         });
       }
     } while ((environment = environment.parent));
 
@@ -590,17 +591,17 @@ VariablesViewController.prototype = {
     if (aTarget._fetched) {
       return aTarget._fetched;
     }
     // Make sure the source grip is available.
     if (!aSource) {
       return promise.reject(new Error("No actor grip was given for the variable."));
     }
 
-    let deferred = promise.defer();
+    let deferred = defer();
     aTarget._fetched = deferred.promise;
 
     if (aSource.type === "property-iterator") {
       return this._populateFromPropertyIterator(aTarget, aSource);
     }
 
     if (aSource.type === "entries-list") {
       return this._populateFromEntries(aTarget, aSource);