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 302458 c93918e6463ac250d76f11f0e93aa76d9f740237
parent 302457 51bc72f4ddcc7857749398cc8c9072587110858e
child 302459 65a02c905ec6c6265944085e4dcf72b8184ba029
push id19745
push usercbook@mozilla.com
push dateFri, 24 Jun 2016 07:04:33 +0000
treeherderfx-team@ed1226de3214 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjryans
bugs1273941
milestone50.0a1
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);