Bug 1273941 - replace uses of promise.defer in devtools/shared; r?jryans draft
authorTom Tromey <tom@tromey.com>
Thu, 09 Jun 2016 09:03:47 -0600
changeset 380904 e529e0ba26237fcfb62b347ed402219bd8d5315c
parent 380903 a7e793ef71a31643eddd1bf7cb7a80b580cb8284
child 380905 eb279cfe804e8d1345c07bb7f05fa5cc4a5dfc8f
push id21339
push userbmo:ttromey@mozilla.com
push dateThu, 23 Jun 2016 14:11:17 +0000
reviewersjryans
bugs1273941
milestone50.0a1
Bug 1273941 - replace uses of promise.defer in devtools/shared; r?jryans MozReview-Commit-ID: 2n3uJ5QKsKL
devtools/shared/DevToolsUtils.js
devtools/shared/apps/app-actor-front.js
devtools/shared/apps/tests/unit/test_webappsActor.js
devtools/shared/defer.js
devtools/shared/discovery/tests/unit/test_discovery.js
devtools/shared/event-emitter.js
devtools/shared/fronts/device.js
devtools/shared/fronts/inspector.js
devtools/shared/fronts/styleeditor.js
devtools/shared/gcli/commands/screenshot.js
devtools/shared/protocol.js
devtools/shared/qrcode/index.js
devtools/shared/qrcode/tests/mochitest/test_decode.html
devtools/shared/security/auth.js
devtools/shared/security/cert.js
devtools/shared/security/socket.js
devtools/shared/security/tests/unit/head_dbg.js
devtools/shared/security/tests/unit/test_encryption.js
devtools/shared/security/tests/unit/test_oob_cert_auth.js
devtools/shared/system.js
devtools/shared/task.js
devtools/shared/tests/unit/test_executeSoon.js
devtools/shared/touch/simulator.js
devtools/shared/transport/packets.js
devtools/shared/transport/stream-utils.js
devtools/shared/transport/tests/unit/head_dbg.js
devtools/shared/transport/tests/unit/test_bulk_error.js
devtools/shared/transport/tests/unit/test_client_server_bulk.js
devtools/shared/transport/tests/unit/test_dbgsocket.js
devtools/shared/transport/tests/unit/test_dbgsocket_connection_drop.js
devtools/shared/transport/tests/unit/test_no_bulk.js
devtools/shared/transport/tests/unit/test_queue.js
devtools/shared/transport/tests/unit/test_transport_bulk.js
devtools/shared/transport/transport.js
devtools/shared/webconsole/client.js
--- a/devtools/shared/DevToolsUtils.js
+++ b/devtools/shared/DevToolsUtils.js
@@ -4,16 +4,17 @@
 
 "use strict";
 
 /* General utilities used throughout devtools. */
 
 var { Ci, Cu, Cc, components } = require("chrome");
 var Services = require("Services");
 var promise = require("promise");
+var defer = require("devtools/shared/defer");
 
 loader.lazyRequireGetter(this, "FileUtils",
                          "resource://gre/modules/FileUtils.jsm", true);
 
 // Re-export the thread-safe utils.
 const ThreadSafeDevToolsUtils = require("./ThreadSafeDevToolsUtils.js");
 for (let key of Object.keys(ThreadSafeDevToolsUtils)) {
   exports[key] = ThreadSafeDevToolsUtils[key];
@@ -45,31 +46,31 @@ exports.executeSoon = function executeSo
 
 /**
  * Waits for the next tick in the event loop.
  *
  * @return Promise
  *         A promise that is resolved after the next tick in the event loop.
  */
 exports.waitForTick = function waitForTick() {
-  let deferred = promise.defer();
+  let deferred = defer();
   exports.executeSoon(deferred.resolve);
   return deferred.promise;
 };
 
 /**
  * Waits for the specified amount of time to pass.
  *
  * @param number aDelay
  *        The amount of time to wait, in milliseconds.
  * @return Promise
  *         A promise that is resolved after the specified amount of time passes.
  */
 exports.waitForTime = function waitForTime(aDelay) {
-  let deferred = promise.defer();
+  let deferred = defer();
   setTimeout(deferred.resolve, aDelay);
   return deferred.promise;
 };
 
 /**
  * Like Array.prototype.forEach, but doesn't cause jankiness when iterating over
  * very large arrays by yielding to the browser and continuing execution on the
  * next tick.
@@ -80,17 +81,17 @@ exports.waitForTime = function waitForTi
  *        The function called on each item in the array. If a promise is
  *        returned by this function, iterating over the array will be paused
  *        until the respective promise is resolved.
  * @returns Promise
  *          A promise that is resolved once the whole array has been iterated
  *          over, and all promises returned by the aFn callback are resolved.
  */
 exports.yieldingEach = function yieldingEach(aArray, aFn) {
-  const deferred = promise.defer();
+  const deferred = defer();
 
   let i = 0;
   let len = aArray.length;
   let outstanding = [deferred.promise];
 
   (function loop() {
     const start = Date.now();
 
@@ -430,17 +431,17 @@ function mainThreadFetch(aURL, aOptions 
   if (aOptions.window) {
     // Respect private browsing.
     channel.loadGroup = aOptions.window.QueryInterface(Ci.nsIInterfaceRequestor)
                           .getInterface(Ci.nsIWebNavigation)
                           .QueryInterface(Ci.nsIDocumentLoader)
                           .loadGroup;
   }
 
-  let deferred = promise.defer();
+  let deferred = defer();
   let onResponse = (stream, status, request) => {
     if (!components.isSuccessCode(status)) {
       deferred.reject(new Error(`Failed to fetch ${url}. Code ${status}.`));
       return;
     }
 
     try {
       // We cannot use NetUtil to do the charset conversion as if charset
@@ -592,17 +593,17 @@ if (!this.isWorker) {
  *         empty array. The reject reason will be forwarded from the first
  *         promise in the list of given promises to be rejected.
  */
 exports.settleAll = values => {
   if (values === null || typeof (values[Symbol.iterator]) != "function") {
     throw new Error("settleAll() expects an iterable.");
   }
 
-  let deferred = promise.defer();
+  let deferred = defer();
 
   values = Array.isArray(values) ? values : [...values];
   let countdown = values.length;
   let resolutionValues = new Array(countdown);
   let rejectionValue;
   let rejectionOccurred = false;
 
   if (!countdown) {
--- a/devtools/shared/apps/app-actor-front.js
+++ b/devtools/shared/apps/app-actor-front.js
@@ -4,16 +4,17 @@
 
 "use strict";
 
 const {Ci, Cc, Cr} = require("chrome");
 const {OS} = require("resource://gre/modules/osfile.jsm");
 const {FileUtils} = require("resource://gre/modules/FileUtils.jsm");
 const {NetUtil} = require("resource://gre/modules/NetUtil.jsm");
 const promise = require("promise");
+const defer = require("devtools/shared/defer");
 const DevToolsUtils = require("devtools/shared/DevToolsUtils");
 const EventEmitter = require("devtools/shared/event-emitter");
 
 // Bug 1188401: When loaded from xpcshell tests, we do not have browser/ files
 // and can't load target.js. Should be fixed by bug 912121.
 loader.lazyRequireGetter(this, "TargetFactory", "devtools/client/framework/target", true);
 
 // XXX: bug 912476 make this module a real protocol.js front
@@ -65,17 +66,17 @@ function getResultText(code) {
   let ex = Cc["@mozilla.org/js/xpc/Exception;1"].
            createInstance(Ci.nsIXPCException);
   ex.initialize(null, code, null, null, null, null);
   let [, message, name] = regexp.exec(ex.toString());
   return { name: name, message: message };
 }
 
 function zipDirectory(zipFile, dirToArchive) {
-  let deferred = promise.defer();
+  let deferred = defer();
   let writer = Cc["@mozilla.org/zipwriter;1"].createInstance(Ci.nsIZipWriter);
   writer.open(zipFile, PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE);
 
   this.addDirToZip(writer, dirToArchive, "");
 
   writer.processQueue({
     onStartRequest: function onStartRequest(request, context) {},
     onStopRequest: (request, context, status) => {
@@ -97,17 +98,17 @@ function uploadPackage(client, webappsAc
   if (client.traits.bulk) {
     return uploadPackageBulk(client, webappsActor, packageFile, progressCallback);
   } else {
     return uploadPackageJSON(client, webappsActor, packageFile, progressCallback);
   }
 }
 
 function uploadPackageJSON(client, webappsActor, packageFile, progressCallback) {
-  let deferred = promise.defer();
+  let deferred = defer();
 
   let request = {
     to: webappsActor,
     type: "uploadPackage"
   };
   client.request(request, (res) => {
     openFile(res.actor);
   });
@@ -168,17 +169,17 @@ function uploadPackageJSON(client, webap
     client.request(request, (res) => {
       deferred.resolve(actor);
     });
   }
   return deferred.promise;
 }
 
 function uploadPackageBulk(client, webappsActor, packageFile, progressCallback) {
-  let deferred = promise.defer();
+  let deferred = defer();
 
   let request = {
     to: webappsActor,
     type: "uploadPackage",
     bulk: true
   };
   client.request(request, (res) => {
     startBulkUpload(res.actor);
@@ -227,17 +228,17 @@ function removeServerTemporaryFile(clien
 /**
  * progressCallback argument:
  * Function called as packaged app installation proceeds.
  * The progress object passed to this function contains:
  *  * bytesSent:  The number of bytes sent so far
  *  * totalBytes: The total number of bytes to send
  */
 function installPackaged(client, webappsActor, packagePath, appId, progressCallback) {
-  let deferred = promise.defer();
+  let deferred = defer();
   let file = FileUtils.File(packagePath);
   let packagePromise;
   if (file.isDirectory()) {
     let tmpZipFile = FileUtils.getDir("TmpD", [], true);
     tmpZipFile.append("application.zip");
     tmpZipFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, parseInt("666", 8));
     packagePromise = zipDirectory(tmpZipFile, file);
   } else {
@@ -275,17 +276,17 @@ function installPackaged(client, webapps
             () => removeServerTemporaryFile(client, fileActor));
         });
   });
   return deferred.promise;
 }
 exports.installPackaged = installPackaged;
 
 function installHosted(client, webappsActor, appId, metadata, manifest) {
-  let deferred = promise.defer();
+  let deferred = defer();
   let request = {
     to: webappsActor,
     type: "install",
     appId: appId,
     metadata: metadata,
     manifest: manifest
   };
   client.request(request, (res) => {
@@ -304,17 +305,17 @@ exports.installHosted = installHosted;
 function getTargetForApp(client, webappsActor, manifestURL) {
   // Ensure always returning the exact same JS object for a target
   // of the same app in order to show only one toolbox per app and
   // avoid re-creating lot of objects twice.
   let existingTarget = appTargets.get(manifestURL);
   if (existingTarget)
     return promise.resolve(existingTarget);
 
-  let deferred = promise.defer();
+  let deferred = defer();
   let request = {
     to: webappsActor,
     type: "getAppActor",
     manifestURL: manifestURL,
   };
   client.request(request, (res) => {
     if (res.error) {
       deferred.reject(res.error);
@@ -376,17 +377,17 @@ function closeApp(client, webappsActor, 
     to: webappsActor,
     type: "close",
     manifestURL: manifestURL
   });
 }
 exports.closeApp = closeApp;
 
 function getTarget(client, form) {
-  let deferred = promise.defer();
+  let deferred = defer();
   let options = {
     form: form,
     client: client,
     chrome: false
   };
 
   TargetFactory.forRemoteTab(options).then((target) => {
     target.isApp = true;
@@ -458,17 +459,17 @@ App.prototype = {
                     this.manifest.manifestURL);
   },
 
   getIcon: function () {
     if (this.iconURL) {
       return promise.resolve(this.iconURL);
     }
 
-    let deferred = promise.defer();
+    let deferred = defer();
 
     let request = {
       to: this.webappsActor,
       type: "getIconAsDataURL",
       manifestURL: this.manifest.manifestURL
     };
 
     this.client.request(request, res => {
@@ -763,17 +764,17 @@ AppActorFront.prototype = {
     return this._install(request);
   },
 
   _onInstallProgress: function (progress) {
     this.emit("install-progress", progress);
   },
 
   _install: function (request) {
-    let deferred = promise.defer();
+    let deferred = defer();
     let finalAppId = null, manifestURL = null;
     let installs = {};
 
     // We need to resolve only once the request is done *AND*
     // once we receive the related appInstall message for
     // the same manifestURL
     let resolve = app => {
       this._unlistenAppEvents(listener);
--- a/devtools/shared/apps/tests/unit/test_webappsActor.js
+++ b/devtools/shared/apps/tests/unit/test_webappsActor.js
@@ -1,12 +1,13 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 const promise = require("promise");
+const defer = require("devtools/shared/defer");
 
 var gAppId = "actor-test";
 const APP_ORIGIN = "app://" + gAppId;
 
 add_test(function testLaunchInexistantApp() {
   let request = {type: "launch", manifestURL: "http://foo.com"};
   webappActorRequest(request, function (aResponse) {
     do_check_eq(aResponse.error, "NO_SUCH_APP");
@@ -172,17 +173,17 @@ add_test(function testUninstall() {
 });
 
 add_test(function testFileUploadInstall() {
   let packageFile = do_get_file("data/app.zip");
 
   // Disable the bulk trait temporarily to test the JSON upload path
   gClient.traits.bulk = false;
 
-  let progressDeferred = promise.defer();
+  let progressDeferred = defer();
   // Ensure we get at least one progress event at the end
   gActorFront.on("install-progress", function onProgress(e, progress) {
     if (progress.bytesSent == progress.totalBytes) {
       gActorFront.off("install-progress", onProgress);
       progressDeferred.resolve();
     }
   });
 
@@ -201,17 +202,17 @@ add_test(function testFileUploadInstall(
       run_next_test();
     });
 });
 
 add_test(function testBulkUploadInstall() {
   let packageFile = do_get_file("data/app.zip");
   do_check_true(gClient.traits.bulk);
 
-  let progressDeferred = promise.defer();
+  let progressDeferred = defer();
   // Ensure we get at least one progress event at the end
   gActorFront.on("install-progress", function onProgress(e, progress) {
     if (progress.bytesSent == progress.totalBytes) {
       gActorFront.off("install-progress", onProgress);
       progressDeferred.resolve();
     }
   });
 
--- a/devtools/shared/defer.js
+++ b/devtools/shared/defer.js
@@ -1,14 +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";
 
+// See bug 1273941 to understand this choice of promise.
+const Promise = require("promise");
+
 /**
  * Returns a deferred object, with a resolve and reject property.
  * https://developer.mozilla.org/en-US/docs/Mozilla/JavaScript_code_modules/Promise.jsm/Deferred
  */
 module.exports = function defer() {
   let resolve, reject;
   let promise = new Promise(function () {
     resolve = arguments[0];
--- a/devtools/shared/discovery/tests/unit/test_discovery.js
+++ b/devtools/shared/discovery/tests/unit/test_discovery.js
@@ -4,16 +4,17 @@
 "use strict";
 
 var Cu = Components.utils;
 
 const { require } =
   Cu.import("resource://devtools/shared/Loader.jsm", {});
 const Services = require("Services");
 const promise = require("promise");
+const defer = require("devtools/shared/defer");
 const EventEmitter = require("devtools/shared/event-emitter");
 const discovery = require("devtools/shared/discovery/discovery");
 const { setTimeout, clearTimeout } = require("sdk/timers");
 
 Services.prefs.setBoolPref("devtools.discovery.log", true);
 
 do_register_cleanup(() => {
   Services.prefs.clearUserPref("devtools.discovery.log");
@@ -127,31 +128,31 @@ add_task(function* () {
   // the service becoming unreachable
   gTestTransports = {};
 
   discovery.removeService("penguins");
   yield scanForChange("penguins", "removed");
 });
 
 function scanForChange(service, changeType) {
-  let deferred = promise.defer();
+  let deferred = defer();
   let timer = setTimeout(() => {
     deferred.reject(new Error("Reply never arrived"));
   }, discovery.replyTimeout + 500);
   discovery.on(service + "-device-" + changeType, function onChange() {
     discovery.off(service + "-device-" + changeType, onChange);
     clearTimeout(timer);
     deferred.resolve();
   });
   discovery.scan();
   return deferred.promise;
 }
 
 function scanForNoChange(service, changeType) {
-  let deferred = promise.defer();
+  let deferred = defer();
   let timer = setTimeout(() => {
     deferred.resolve();
   }, discovery.replyTimeout + 500);
   discovery.on(service + "-device-" + changeType, function onChange() {
     discovery.off(service + "-device-" + changeType, onChange);
     clearTimeout(timer);
     deferred.reject(new Error("Unexpected change occurred"));
   });
--- a/devtools/shared/event-emitter.js
+++ b/devtools/shared/event-emitter.js
@@ -12,18 +12,18 @@
     // Cu.import
     this.isWorker = false;
     // Bug 1259045: This module is loaded early in firefox startup as a JSM,
     // but it doesn't depends on any real module. We can save a few cycles
     // and bytes by not loading Loader.jsm.
     let require = function (module) {
       const Cu = Components.utils;
       switch (module) {
-        case "promise":
-          return Cu.import("resource://gre/modules/Promise.jsm", {}).Promise;
+        case "devtools/shared/defer":
+          return Cu.import("resource://gre/modules/Promise.jsm", {}).Promise.defer;
         case "Services":
           return Cu.import("resource://gre/modules/Services.jsm", {}).Services;
         case "chrome":
           return {
             Cu,
             components: Components
           };
       }
@@ -34,17 +34,17 @@
   }
 }).call(this, function (require, exports, module) {
   let EventEmitter = this.EventEmitter = function () {};
   module.exports = EventEmitter;
 
   // See comment in JSM module boilerplate when adding a new dependency.
   const { components } = require("chrome");
   const Services = require("Services");
-  const promise = require("promise");
+  const defer = require("devtools/shared/defer");
   let loggingEnabled = true;
 
   if (!isWorker) {
     loggingEnabled = Services.prefs.getBoolPref("devtools.dump.emit");
     Services.prefs.addObserver("devtools.dump.emit", {
       observe: () => {
         loggingEnabled = Services.prefs.getBoolPref("devtools.dump.emit");
       }
@@ -95,17 +95,17 @@
      *        one time.
      * @return promise
      *        A promise which is resolved when the event next happens. The
      *        resolution value of the promise is the first event argument. If
      *        you need access to second or subsequent event arguments (it's rare
      *        that this is needed) then use listener
      */
     once(event, listener) {
-      let deferred = promise.defer();
+      let deferred = defer();
 
       let handler = (_, first, ...rest) => {
         this.off(event, handler);
         if (listener) {
           listener.apply(null, [event, first, ...rest]);
         }
         deferred.resolve(first);
       };
--- a/devtools/shared/fronts/device.js
+++ b/devtools/shared/fronts/device.js
@@ -1,29 +1,29 @@
 /* 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 {deviceSpec} = require("devtools/shared/specs/device");
 const protocol = require("devtools/shared/protocol");
-const promise = require("promise");
+const defer = require("devtools/shared/defer");
 
 const DeviceFront = protocol.FrontClassWithSpec(deviceSpec, {
   initialize: function (client, form) {
     protocol.Front.prototype.initialize.call(this, client);
     this.actorID = form.deviceActor;
     this.manage(this);
   },
 
   screenshotToBlob: function () {
     return this.screenshotToDataURL().then(longstr => {
       return longstr.string().then(dataURL => {
-        let deferred = promise.defer();
+        let deferred = defer();
         longstr.release().then(null, Cu.reportError);
         let req = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
             .createInstance(Ci.nsIXMLHttpRequest);
         req.open("GET", dataURL, true);
         req.responseType = "blob";
         req.onload = () => {
           deferred.resolve(req.response);
         };
--- a/devtools/shared/fronts/inspector.js
+++ b/devtools/shared/fronts/inspector.js
@@ -15,16 +15,17 @@ const {
 } = require("devtools/shared/protocol.js");
 const {
   inspectorSpec,
   nodeSpec,
   nodeListSpec,
   walkerSpec
 } = require("devtools/shared/specs/inspector");
 const promise = require("promise");
+const defer = require("devtools/shared/defer");
 const { Task } = require("devtools/shared/task");
 const { Class } = require("sdk/core/heritage");
 const events = require("sdk/event/core");
 const object = require("sdk/util/object");
 const nodeConstants = require("devtools/shared/dom-node-constants.js");
 
 const HIDDEN_CLASS = "__fx-devtools-hide-shortcut__";
 
@@ -526,17 +527,17 @@ const WalkerFront = FrontClassWithSpec(w
     return this._rootNodeDeferred.promise;
   },
 
   /**
    * Create the root node promise, triggering the "new-root" notification
    * on resolution.
    */
   _createRootNodePromise: function () {
-    this._rootNodeDeferred = promise.defer();
+    this._rootNodeDeferred = defer();
     this._rootNodeDeferred.promise.then(() => {
       events.emit(this, "new-root");
     });
   },
 
   /**
    * When reading an actor form off the wire, we want to hook it up to its
    * parent front.  The protocol guarantees that the parent will be seen
--- a/devtools/shared/fronts/styleeditor.js
+++ b/devtools/shared/fronts/styleeditor.js
@@ -5,16 +5,17 @@
 
 const { SimpleStringFront } = require("devtools/shared/fronts/string");
 const { Front, FrontClassWithSpec } = require("devtools/shared/protocol");
 const {
   oldStyleSheetSpec,
   styleEditorSpec
 } = require("devtools/shared/specs/styleeditor");
 const promise = require("promise");
+const defer = require("devtools/shared/defer");
 const events = require("sdk/event/core");
 
 /**
  * StyleSheetFront is the client-side counterpart to a StyleSheetActor.
  */
 const OldStyleSheetFront = FrontClassWithSpec(oldStyleSheetSpec, {
   initialize: function (conn, form, ctx, detail) {
     Front.prototype.initialize.call(this, conn, form, ctx, detail);
@@ -38,17 +39,17 @@ const OldStyleSheetFront = FrontClassWit
       this.actorID = form;
       return;
     }
     this.actorID = form.actor;
     this._form = form;
   },
 
   getText: function () {
-    let deferred = promise.defer();
+    let deferred = defer();
 
     events.once(this, "source-load", (source) => {
       let longStr = new SimpleStringFront(source);
       deferred.resolve(longStr);
     });
     this.fetchSource();
 
     return deferred.promise;
@@ -89,17 +90,17 @@ exports.OldStyleSheetFront = OldStyleShe
 const StyleEditorFront = FrontClassWithSpec(styleEditorSpec, {
   initialize: function (client, tabForm) {
     Front.prototype.initialize.call(this, client);
     this.actorID = tabForm.styleEditorActor;
     this.manage(this);
   },
 
   getStyleSheets: function () {
-    let deferred = promise.defer();
+    let deferred = defer();
 
     events.once(this, "document-load", (styleSheets) => {
       deferred.resolve(styleSheets);
     });
     this.newDocument();
 
     return deferred.promise;
   },
--- a/devtools/shared/gcli/commands/screenshot.js
+++ b/devtools/shared/gcli/commands/screenshot.js
@@ -5,16 +5,17 @@
 "use strict";
 
 const { Cc, Ci, Cr } = require("chrome");
 const l10n = require("gcli/l10n");
 const Services = require("Services");
 const { NetUtil } = require("resource://gre/modules/NetUtil.jsm");
 const { getRect } = require("devtools/shared/layout/utils");
 const promise = require("promise");
+const defer = require("devtools/shared/defer");
 const { Task } = require("devtools/shared/task");
 
 loader.lazyImporter(this, "Downloads", "resource://gre/modules/Downloads.jsm");
 loader.lazyImporter(this, "OS", "resource://gre/modules/osfile.jsm");
 loader.lazyImporter(this, "FileUtils", "resource://gre/modules/FileUtils.jsm");
 loader.lazyImporter(this, "PrivateBrowsingUtils",
                           "resource://gre/modules/PrivateBrowsingUtils.jsm");
 
@@ -459,17 +460,17 @@ function DownloadListener(win, transfer)
   for (let name in transfer) {
     if (name != "QueryInterface" &&
         name != "onStateChange") {
       this[name] = (...args) => transfer[name].apply(transfer, args);
     }
   }
 
   // Allow saveToFile to await completion for error handling
-  this._completedDeferred = promise.defer();
+  this._completedDeferred = defer();
   this.completed = this._completedDeferred.promise;
 }
 
 DownloadListener.prototype = {
   QueryInterface: function(iid) {
     if (iid.equals(Ci.nsIInterfaceRequestor) ||
         iid.equals(Ci.nsIWebProgressListener) ||
         iid.equals(Ci.nsIWebProgressListener2) ||
--- a/devtools/shared/protocol.js
+++ b/devtools/shared/protocol.js
@@ -3,16 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 var { Cu, components } = require("chrome");
 var DevToolsUtils = require("devtools/shared/DevToolsUtils");
 var Services = require("Services");
 var promise = require("promise");
+var defer = require("devtools/shared/defer");
 var {Class} = require("sdk/core/heritage");
 var {EventTarget} = require("sdk/event/target");
 var events = require("sdk/event/core");
 var object = require("sdk/util/object");
 
 exports.emit = events.emit;
 
 /**
@@ -1208,17 +1209,17 @@ var Front = Class({
       }).then(null, e => DevToolsUtils.reportException("Front.prototype.send", e));
     }
   },
 
   /**
    * Send a two-way request on the connection.
    */
   request: function (packet) {
-    let deferred = promise.defer();
+    let deferred = defer();
     // Save packet basics for debugging
     let { to, type } = packet;
     this._requests.push({
       deferred,
       to: to || this.actorID,
       type,
       stack: components.stack,
     });
--- a/devtools/shared/qrcode/index.js
+++ b/devtools/shared/qrcode/index.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 { Cu } = require("chrome");
 const promise = require("promise");
+const defer = require("devtools/shared/defer");
 
 // Lazily require encoder and decoder in case only one is needed
 Object.defineProperty(this, "Encoder", {
   get: () => require("./encoder/index").Encoder
 });
 Object.defineProperty(this, "QRRSBlock", {
   get: () => require("./encoder/index").QRRSBlock
 });
@@ -90,17 +91,17 @@ exports.encodeToDataURI = function (mess
  * @return Promise
  *         The promise will be resolved with a string, which is the data inside
  *         the QR code.
  */
 exports.decodeFromURI = function (URI) {
   if (!decoder) {
     return promise.reject();
   }
-  let deferred = promise.defer();
+  let deferred = defer();
   decoder.decodeFromURI(URI, deferred.resolve, deferred.reject);
   return deferred.promise;
 };
 
 /**
  * Decode a QR code that has been drawn to a canvas element.
  * @param Canvas canvas
  *        <canvas> element to read from
--- a/devtools/shared/qrcode/tests/mochitest/test_decode.html
+++ b/devtools/shared/qrcode/tests/mochitest/test_decode.html
@@ -10,16 +10,17 @@ Test decoding a simple message
   <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
   <script type="application/javascript;version=1.8">
 window.onload = function() {
   const { utils: Cu } = Components;
   const { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
   const { Task } = require("devtools/shared/task");
   const promise = require("promise");
+  const defer = require("devtools/shared/defer");
 
   const QR = require("devtools/shared/qrcode/index");
 
   SimpleTest.waitForExplicitFinish();
 
   const testImage =
     "data:image/gif;base64,R0lGODdhOgA6AIAAAAAAAP///ywAAAAAOgA6AAAC" +
     "/4yPqcvtD6OctNqLs968+w+G4gKU5nkaKKquLuW+QVy2tAkDTj3rfQts8CRDko" +
@@ -34,17 +35,17 @@ window.onload = function() {
     let result = yield QR.decodeFromURI(testImage);
     is(result, "HELLO", "Decoded data URI result matches");
     let canvas = yield drawToCanvas(testImage);
     result = QR.decodeFromCanvas(canvas);
     is(result, "HELLO", "Decoded canvas result matches");
   }).then(SimpleTest.finish, ok.bind(null, false));
 
   function drawToCanvas(src) {
-    let deferred = promise.defer();
+    let deferred = defer();
     let canvas = document.createElement("canvas");
     let context = canvas.getContext("2d");
     let image = new Image();
 
     image.onload = () => {
       canvas.width = image.width;
       canvas.height = image.height;
       context.drawImage(image, 0, 0);
--- a/devtools/shared/security/auth.js
+++ b/devtools/shared/security/auth.js
@@ -4,16 +4,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";
 
 var { Ci, Cc } = require("chrome");
 var Services = require("Services");
 var promise = require("promise");
+var defer = require("devtools/shared/defer");
 var DevToolsUtils = require("devtools/shared/DevToolsUtils");
 var { dumpn, dumpv } = DevToolsUtils;
 loader.lazyRequireGetter(this, "prompt",
   "devtools/shared/security/prompt");
 loader.lazyRequireGetter(this, "cert",
   "devtools/shared/security/cert");
 loader.lazyRequireGetter(this, "asyncStorage",
   "devtools/shared/async-storage");
@@ -306,17 +307,17 @@ OOBCert.Client.prototype = {
    *        Whether the server requires encryption.  Defaults to false.
    * @param cert object (optional)
    *        The server's cert details.
    * @param transport DebuggerTransport
    *        A transport that can be used to communicate with the server.
    * @return A promise can be used if there is async behavior.
    */
   authenticate({ host, port, cert, transport }) {
-    let deferred = promise.defer();
+    let deferred = defer();
     let oobData;
 
     let activeSendDialog;
     let closeDialog = () => {
       // Close any prompts the client may have been showing from previous
       // authentication steps
       if (activeSendDialog && activeSendDialog.close) {
         activeSendDialog.close();
--- a/devtools/shared/security/cert.js
+++ b/devtools/shared/security/cert.js
@@ -3,16 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 var { Ci, Cc } = require("chrome");
 var promise = require("promise");
+var defer = require("devtools/shared/defer");
 var DevToolsUtils = require("devtools/shared/DevToolsUtils");
 DevToolsUtils.defineLazyGetter(this, "localCertService", () => {
   // Ensure PSM is initialized to support TLS sockets
   Cc["@mozilla.org/psm;1"].getService(Ci.nsISupports);
   return Cc["@mozilla.org/security/local-cert-service;1"]
          .getService(Ci.nsILocalCertService);
 });
 
@@ -26,17 +27,17 @@ exports.local = {
    *
    * The cert is stored permanently in the profile's key store after first use,
    * and is valid for 1 year.  If an expired or otherwise invalid cert is found,
    * it is removed and a new one is made.
    *
    * @return promise
    */
   getOrCreate() {
-    let deferred = promise.defer();
+    let deferred = defer();
     localCertService.getOrCreateCert(localCertName, {
       handleCert: function (cert, rv) {
         if (rv) {
           deferred.reject(rv);
           return;
         }
         deferred.resolve(cert);
       }
@@ -45,17 +46,17 @@ exports.local = {
   },
 
   /**
    * Remove the DevTools self-signed X.509 cert for this device.
    *
    * @return promise
    */
   remove() {
-    let deferred = promise.defer();
+    let deferred = defer();
     localCertService.removeCert(localCertName, {
       handleCert: function (rv) {
         if (rv) {
           deferred.reject(rv);
           return;
         }
         deferred.resolve();
       }
--- a/devtools/shared/security/socket.js
+++ b/devtools/shared/security/socket.js
@@ -8,16 +8,17 @@
 
 var { Ci, Cc, CC, Cr, Cu } = require("chrome");
 
 // Ensure PSM is initialized to support TLS sockets
 Cc["@mozilla.org/psm;1"].getService(Ci.nsISupports);
 
 var Services = require("Services");
 var promise = require("promise");
+var defer = require("devtools/shared/defer");
 var DevToolsUtils = require("devtools/shared/DevToolsUtils");
 var { dumpn, dumpv } = DevToolsUtils;
 loader.lazyRequireGetter(this, "DebuggerTransport",
   "devtools/shared/transport/transport", true);
 loader.lazyRequireGetter(this, "DebuggerServer",
   "devtools/server/main", true);
 loader.lazyRequireGetter(this, "discovery",
   "devtools/shared/discovery/discovery");
@@ -228,17 +229,17 @@ var _attemptConnect = Task.async(functio
 
   // If encrypting, load the client cert now, so we can deliver it at just the
   // right time.
   let clientCert;
   if (encryption) {
     clientCert = yield cert.local.getOrCreate();
   }
 
-  let deferred = promise.defer();
+  let deferred = defer();
   let input;
   let output;
   // Delay opening the input stream until the transport has fully connected.
   // The goal is to avoid showing the user a client cert UI prompt when
   // encryption is used.  This prompt is shown when the client opens the input
   // stream and does not know which client cert to present to the server.  To
   // specify a client cert programmatically, we need to access the transport's
   // nsISSLSocketControl interface, which is not accessible until the transport
@@ -285,17 +286,17 @@ var _attemptConnect = Task.async(functio
 });
 
 /**
  * Check if the input stream is alive.  For an encrypted connection, it may not
  * be if the client refuses the server's cert.  A cert error is expected on
  * first connection to a new host because the cert is self-signed.
  */
 function _isInputAlive(input) {
-  let deferred = promise.defer();
+  let deferred = defer();
   input.asyncWait({
     onInputStreamReady(stream) {
       try {
         stream.available();
         deferred.resolve({ alive: true });
       } catch (e) {
         try {
           // getErrorClass may throw if you pass a non-NSS error
@@ -633,17 +634,17 @@ ServerSocketConnection.prototype = {
   },
 
   /**
    * When encryption is used, we wait for the client to complete the TLS
    * handshake before proceeding.  The handshake details are validated in
    * |onHandshakeDone|.
    */
   _listenForTLSHandshake() {
-    this._handshakeDeferred = promise.defer();
+    this._handshakeDeferred = defer();
     if (!this._listener.encryption) {
       this._handshakeDeferred.resolve();
       return;
     }
     this._setSecurityObserver(this);
     this._handshakeTimeout = setTimeout(this._onHandshakeTimeout.bind(this),
                                         HANDSHAKE_TIMEOUT);
   },
--- a/devtools/shared/security/tests/unit/head_dbg.js
+++ b/devtools/shared/security/tests/unit/head_dbg.js
@@ -6,16 +6,17 @@ var Cc = Components.classes;
 var Ci = Components.interfaces;
 var Cu = Components.utils;
 var Cr = Components.results;
 var CC = Components.Constructor;
 
 const { require } =
   Cu.import("resource://devtools/shared/Loader.jsm", {});
 const promise = require("promise");
+const defer = require("devtools/shared/defer");
 const { Task } = require("devtools/shared/task");
 
 const Services = require("Services");
 const DevToolsUtils = require("devtools/shared/DevToolsUtils");
 const xpcInspector = require("xpcInspector");
 const { DebuggerServer } = require("devtools/server/main");
 const { DebuggerClient } = require("devtools/shared/client/main");
 
--- a/devtools/shared/security/tests/unit/test_encryption.js
+++ b/devtools/shared/security/tests/unit/test_encryption.js
@@ -8,17 +8,17 @@ function run_test() {
   // Need profile dir to store the key / cert
   do_get_profile();
   // Ensure PSM is initialized
   Cc["@mozilla.org/psm;1"].getService(Ci.nsISupports);
   run_next_test();
 }
 
 function connectClient(client) {
-  let deferred = promise.defer();
+  let deferred = defer();
   client.connect(() => {
     client.listTabs(deferred.resolve);
   });
   return deferred.promise;
 }
 
 add_task(function* () {
   initTestDebuggerServer();
--- a/devtools/shared/security/tests/unit/test_oob_cert_auth.js
+++ b/devtools/shared/security/tests/unit/test_oob_cert_auth.js
@@ -27,17 +27,17 @@ add_task(function* () {
 
 // Client w/ OOB_CERT auth connects successfully to server w/ OOB_CERT auth
 add_task(function* () {
   equal(DebuggerServer.listeningSockets, 0, "0 listening sockets");
 
   // Grab our cert, instead of relying on a discovery advertisement
   let serverCert = yield cert.local.getOrCreate();
 
-  let oobData = promise.defer();
+  let oobData = defer();
   let AuthenticatorType = DebuggerServer.Authenticators.get("OOB_CERT");
   let serverAuth = new AuthenticatorType.Server();
   serverAuth.allowConnection = () => {
     return DebuggerServer.AuthenticationResult.ALLOW;
   };
   serverAuth.receiveOOB = () => oobData.promise; // Skip prompt for tests
 
   let listener = DebuggerServer.createListener();
@@ -87,17 +87,17 @@ add_task(function* () {
   listener.close();
   equal(DebuggerServer.listeningSockets, 0, "0 listening sockets");
 });
 
 // Client w/o OOB_CERT auth fails to connect to server w/ OOB_CERT auth
 add_task(function* () {
   equal(DebuggerServer.listeningSockets, 0, "0 listening sockets");
 
-  let oobData = promise.defer();
+  let oobData = defer();
   let AuthenticatorType = DebuggerServer.Authenticators.get("OOB_CERT");
   let serverAuth = new AuthenticatorType.Server();
   serverAuth.allowConnection = () => {
     return DebuggerServer.AuthenticationResult.ALLOW;
   };
   serverAuth.receiveOOB = () => oobData.promise; // Skip prompt for tests
 
   let listener = DebuggerServer.createListener();
@@ -113,17 +113,17 @@ add_task(function* () {
   let transport = yield DebuggerClient.socketConnect({
     host: "127.0.0.1",
     port: listener.port,
     encryption: true
     // authenticator: PROMPT is the default
   });
 
   // Attempt to use the transport
-  let deferred = promise.defer();
+  let deferred = defer();
   let client = new DebuggerClient(transport);
   client.onPacket = packet => {
     // Client did not authenticate, so it ends up seeing the server's auth data
     // which is effectively malformed data from the client's perspective
     ok(!packet.from && packet.authResult, "Got auth packet instead of data");
     deferred.resolve();
   };
   client.connect();
@@ -150,17 +150,17 @@ add_task(function* () {
 
 // Client w/ invalid K value fails to connect
 add_task(function* () {
   equal(DebuggerServer.listeningSockets, 0, "0 listening sockets");
 
   // Grab our cert, instead of relying on a discovery advertisement
   let serverCert = yield cert.local.getOrCreate();
 
-  let oobData = promise.defer();
+  let oobData = defer();
   let AuthenticatorType = DebuggerServer.Authenticators.get("OOB_CERT");
   let serverAuth = new AuthenticatorType.Server();
   serverAuth.allowConnection = () => {
     return DebuggerServer.AuthenticationResult.ALLOW;
   };
   serverAuth.receiveOOB = () => oobData.promise; // Skip prompt for tests
 
   let clientAuth = new AuthenticatorType.Client();
@@ -204,17 +204,17 @@ add_task(function* () {
 
 // Client w/ invalid cert hash fails to connect
 add_task(function* () {
   equal(DebuggerServer.listeningSockets, 0, "0 listening sockets");
 
   // Grab our cert, instead of relying on a discovery advertisement
   let serverCert = yield cert.local.getOrCreate();
 
-  let oobData = promise.defer();
+  let oobData = defer();
   let AuthenticatorType = DebuggerServer.Authenticators.get("OOB_CERT");
   let serverAuth = new AuthenticatorType.Server();
   serverAuth.allowConnection = () => {
     return DebuggerServer.AuthenticationResult.ALLOW;
   };
   serverAuth.receiveOOB = () => oobData.promise; // Skip prompt for tests
 
   let clientAuth = new AuthenticatorType.Client();
--- a/devtools/shared/system.js
+++ b/devtools/shared/system.js
@@ -3,16 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 const { Cc, Ci, Cu } = require("chrome");
 const { Task } = require("devtools/shared/task");
 
 loader.lazyRequireGetter(this, "Services");
 loader.lazyRequireGetter(this, "promise");
+loader.lazyRequireGetter(this, "defer", "devtools/shared/defer");
 loader.lazyRequireGetter(this, "OS", "resource://gre/modules/commonjs/node/os.js");
 loader.lazyRequireGetter(this, "DebuggerServer", "devtools/server/main", true);
 loader.lazyRequireGetter(this, "AppConstants",
   "resource://gre/modules/AppConstants.jsm", true);
 loader.lazyGetter(this, "screenManager", () => {
   return Cc["@mozilla.org/gfx/screenmanager;1"].getService(Ci.nsIScreenManager);
 });
 loader.lazyGetter(this, "oscpu", () => {
@@ -302,17 +303,17 @@ function getOSCPU() {
   if (oscpu.includes("NT 10.")) {
     return 7;
   }
   // Other OS.
   return 12;
 }
 
 function getSetting(name) {
-  let deferred = promise.defer();
+  let deferred = defer();
 
   if ("@mozilla.org/settingsService;1" in Cc) {
     let settingsService;
 
     // settingsService fails in b2g child processes
     // TODO bug 1205797, make this work in child processes.
     try {
       settingsService = Cc["@mozilla.org/settingsService;1"].getService(Ci.nsISettingsService);
--- a/devtools/shared/task.js
+++ b/devtools/shared/task.js
@@ -83,16 +83,17 @@
  *   or a synchronous function.  This comes in handy when iterating over
  *   function lists where some items have been converted to tasks and some not.
  */
 
 ////////////////////////////////////////////////////////////////////////////////
 //// Globals
 
 const Promise = require("promise");
+const defer = require("devtools/shared/defer");
 
 // The following error types are considered programmer errors, which should be
 // reported (possibly redundantly) so as to let programmers fix their code.
 const ERRORS_TO_REPORT = ["EvalError", "RangeError", "ReferenceError",
                           "TypeError"];
 
 /**
  * The Task currently being executed
@@ -261,17 +262,17 @@ function createAsyncFunction(task) {
 /**
  * Executes the specified iterator as a task, and gives access to the promise
  * that is fulfilled when the task terminates.
  */
 function TaskImpl(iterator) {
   if (gMaintainStack) {
     this._stack = (new Error()).stack;
   }
-  this.deferred = Promise.defer();
+  this.deferred = defer();
   this._iterator = iterator;
   this._isStarGenerator = !("send" in iterator);
   this._run(true);
 }
 
 TaskImpl.prototype = {
   /**
    * Includes the promise object where task completion callbacks are registered,
--- a/devtools/shared/tests/unit/test_executeSoon.js
+++ b/devtools/shared/tests/unit/test_executeSoon.js
@@ -7,16 +7,17 @@
  * Client request stacks should span the entire process from before making the
  * request to handling the reply from the server.  The server frames are not
  * included, nor can they be in most cases, since the server can be a remote
  * device.
  */
 
 var { executeSoon } = require("devtools/shared/DevToolsUtils");
 var promise = require("promise");
+var defer = require("devtools/shared/defer");
 var Services = require("Services");
 
 var asyncStackEnabled =
   Services.prefs.getBoolPref("javascript.options.asyncstack");
 
 do_register_cleanup(() => {
   Services.prefs.setBoolPref("javascript.options.asyncstack",
                              asyncStackEnabled);
@@ -36,12 +37,12 @@ add_task(function* () {
       return;
     }
     stack = stack.asyncCaller || stack.caller;
   }
   ok(false, "Incomplete stack");
 });
 
 function waitForTick() {
-  let deferred = promise.defer();
+  let deferred = defer();
   executeSoon(deferred.resolve);
   return deferred.promise;
 }
--- a/devtools/shared/touch/simulator.js
+++ b/devtools/shared/touch/simulator.js
@@ -1,14 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 var promise = require("promise");
+var defer = require("devtools/shared/defer");
 var Services = require("Services");
 
 const FRAME_SCRIPT =
   "resource://devtools/shared/touch/simulator-content.js";
 
 var trackedBrowsers = new WeakMap();
 var savedTouchEventsEnabled =
   Services.prefs.getIntPref("dom.w3c_touch_events.enabled");
@@ -31,17 +32,17 @@ function TouchEventSimulator(browser) {
     enabled: false,
 
     start() {
       if (this.enabled) {
         return promise.resolve({ isReloadNeeded: false });
       }
       this.enabled = true;
 
-      let deferred = promise.defer();
+      let deferred = defer();
       let isReloadNeeded =
         Services.prefs.getIntPref("dom.w3c_touch_events.enabled") != 1;
       Services.prefs.setIntPref("dom.w3c_touch_events.enabled", 1);
       let onStarted = () => {
         mm.removeMessageListener("TouchEventSimulator:Started", onStarted);
         deferred.resolve({ isReloadNeeded });
       };
       mm.addMessageListener("TouchEventSimulator:Started", onStarted);
@@ -50,17 +51,17 @@ function TouchEventSimulator(browser) {
     },
 
     stop() {
       if (!this.enabled) {
         return promise.resolve();
       }
       this.enabled = false;
 
-      let deferred = promise.defer();
+      let deferred = defer();
       Services.prefs.setIntPref("dom.w3c_touch_events.enabled",
                                 savedTouchEventsEnabled);
       let onStopped = () => {
         mm.removeMessageListener("TouchEventSimulator:Stopped", onStopped);
         deferred.resolve();
       };
       mm.addMessageListener("TouchEventSimulator:Stopped", onStopped);
       mm.sendAsyncMessage("TouchEventSimulator:Stop");
--- a/devtools/shared/transport/packets.js
+++ b/devtools/shared/transport/packets.js
@@ -24,16 +24,17 @@
  *     Called to clean up at the end of use
  */
 
 const { Cc, Ci, Cu } = require("chrome");
 const DevToolsUtils = require("devtools/shared/DevToolsUtils");
 const { dumpn, dumpv } = DevToolsUtils;
 const StreamUtils = require("devtools/shared/transport/stream-utils");
 const promise = require("promise");
+const defer = require("devtools/shared/defer");
 
 DevToolsUtils.defineLazyGetter(this, "unicodeConverter", () => {
   const unicodeConverter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
                            .createInstance(Ci.nsIScriptableUnicodeConverter);
   unicodeConverter.charset = "UTF-8";
   return unicodeConverter;
 });
 
@@ -220,17 +221,17 @@ exports.JSONPacket = JSONPacket;
  * packet's type.  See the Remote Debugging Protocol Stream Transport spec for
  * more details.
  * @param transport DebuggerTransport
  *        The transport instance that will own the packet.
  */
 function BulkPacket(transport) {
   Packet.call(this, transport);
   this._done = false;
-  this._readyForWriting = promise.defer();
+  this._readyForWriting = defer();
 }
 
 /**
  * Attempt to initialize a new BulkPacket based on the incoming packet header
  * we've received so far.
  * @param header string
  *        The packet header string to attempt parsing.
  * @param transport DebuggerTransport
@@ -260,17 +261,17 @@ BulkPacket.HEADER_PATTERN = /^bulk ([^: 
 BulkPacket.prototype = Object.create(Packet.prototype);
 
 BulkPacket.prototype.read = function (stream) {
   dumpv("Reading bulk packet, handing off input stream");
 
   // Temporarily pause monitoring of the input stream
   this._transport.pauseIncoming();
 
-  let deferred = promise.defer();
+  let deferred = defer();
 
   this._transport._onBulkReadReady({
     actor: this.actor,
     type: this.type,
     length: this.length,
     copyTo: (output) => {
       dumpv("CT length: " + this.length);
       let copying = StreamUtils.copyStream(stream, output, this.length);
@@ -313,17 +314,17 @@ BulkPacket.prototype.write = function (s
     return;
   }
 
   dumpv("Handing off output stream");
 
   // Temporarily pause the monitoring of the output stream
   this._transport.pauseOutgoing();
 
-  let deferred = promise.defer();
+  let deferred = defer();
 
   this._readyForWriting.resolve({
     copyFrom: (input) => {
       dumpv("CF length: " + this.length);
       let copying = StreamUtils.copyStream(input, stream, this.length);
       deferred.resolve(copying);
       return copying;
     },
--- a/devtools/shared/transport/stream-utils.js
+++ b/devtools/shared/transport/stream-utils.js
@@ -5,16 +5,17 @@
 "use strict";
 
 const { Ci, Cc, Cu, Cr, CC } = require("chrome");
 const Services = require("Services");
 const DevToolsUtils = require("devtools/shared/DevToolsUtils");
 const { dumpv } = DevToolsUtils;
 const EventEmitter = require("devtools/shared/event-emitter");
 const promise = require("promise");
+const defer = require("devtools/shared/defer");
 
 DevToolsUtils.defineLazyGetter(this, "IOUtil", () => {
   return Cc["@mozilla.org/io-util;1"].getService(Ci.nsIIOUtil);
 });
 
 DevToolsUtils.defineLazyGetter(this, "ScriptableInputStream", () => {
   return CC("@mozilla.org/scriptableinputstream;1",
             "nsIScriptableInputStream", "init");
@@ -70,17 +71,17 @@ function StreamCopier(input, output, len
     this.output = output;
   } else {
     this.output = Cc["@mozilla.org/network/buffered-output-stream;1"].
                   createInstance(Ci.nsIBufferedOutputStream);
     this.output.init(output, BUFFER_SIZE);
   }
   this._length = length;
   this._amountLeft = length;
-  this._deferred = promise.defer();
+  this._deferred = defer();
 
   this._copy = this._copy.bind(this);
   this._flush = this._flush.bind(this);
   this._destroy = this._destroy.bind(this);
 
   // Copy promise's then method up to this object.
   // Allows the copier to offer a promise interface for the simple succeed or
   // fail scenarios, but also emit events (due to the EventEmitter) for other
--- a/devtools/shared/transport/tests/unit/head_dbg.js
+++ b/devtools/shared/transport/tests/unit/head_dbg.js
@@ -8,16 +8,17 @@ var Ci = Components.interfaces;
 var Cu = Components.utils;
 var Cr = Components.results;
 var CC = Components.Constructor;
 
 const { require } =
   Cu.import("resource://devtools/shared/Loader.jsm", {});
 const { NetUtil } = require("resource://gre/modules/NetUtil.jsm");
 const promise = require("promise");
+const defer = require("devtools/shared/defer");
 const { Task } = require("devtools/shared/task");
 
 const Services = require("Services");
 const DevToolsUtils = require("devtools/shared/DevToolsUtils");
 
 // We do not want to log packets by default, because in some tests,
 // we can be sending large amounts of data. The test harness has
 // trouble dealing with logging all the data, and we end up with
--- a/devtools/shared/transport/tests/unit/test_bulk_error.js
+++ b/devtools/shared/transport/tests/unit/test_bulk_error.js
@@ -66,17 +66,17 @@ function json_reply(client, response) {
 
   let request = client.startBulkRequest({
     actor: response.testBulk,
     type: "jsonReply",
     length: reallyLong.length
   });
 
   // Send bulk data to server
-  let copyDeferred = promise.defer();
+  let copyDeferred = defer();
   request.on("bulk-send-ready", ({writer, done}) => {
     let input = Cc["@mozilla.org/io/string-input-stream;1"].
                   createInstance(Ci.nsIStringInputStream);
     input.setData(reallyLong, reallyLong.length);
     try {
       writer.copyFrom(input, () => {
         input.close();
         done();
--- a/devtools/shared/transport/tests/unit/test_client_server_bulk.js
+++ b/devtools/shared/transport/tests/unit/test_client_server_bulk.js
@@ -97,27 +97,27 @@ function add_test_bulk_actor() {
 }
 
 /** * Reply Handlers ***/
 
 var replyHandlers = {
 
   json: function (request) {
     // Receive JSON reply from server
-    let replyDeferred = promise.defer();
+    let replyDeferred = defer();
     request.on("json-reply", (reply) => {
       do_check_true(reply.allDone);
       replyDeferred.resolve();
     });
     return replyDeferred.promise;
   },
 
   bulk: function (request) {
     // Receive bulk data reply from server
-    let replyDeferred = promise.defer();
+    let replyDeferred = defer();
     request.on("bulk-reply", ({length, copyTo}) => {
       do_check_eq(length, really_long().length);
 
       let outputFile = getTestTempFile("bulk-output", true);
       outputFile.create(Ci.nsIFile.NORMAL_FILE_TYPE, parseInt("666", 8));
 
       let output = FileUtils.openSafeFileOutputStream(outputFile);
 
@@ -133,19 +133,19 @@ var replyHandlers = {
 
 /** * Tests ***/
 
 var test_bulk_request_cs = Task.async(function* (transportFactory, actorType, replyType) {
   // Ensure test files are not present from a failed run
   cleanup_files();
   writeTestTempFile("bulk-input", really_long());
 
-  let clientDeferred = promise.defer();
-  let serverDeferred = promise.defer();
-  let bulkCopyDeferred = promise.defer();
+  let clientDeferred = defer();
+  let serverDeferred = defer();
+  let bulkCopyDeferred = defer();
 
   let transport = yield transportFactory();
 
   let client = new DebuggerClient(transport);
   client.connect().then(([app, traits]) => {
     do_check_eq(traits.bulk, true);
     client.listTabs(clientDeferred.resolve);
   });
@@ -190,18 +190,18 @@ var test_bulk_request_cs = Task.async(fu
   ]);
 });
 
 var test_json_request_cs = Task.async(function* (transportFactory, actorType, replyType) {
   // Ensure test files are not present from a failed run
   cleanup_files();
   writeTestTempFile("bulk-input", really_long());
 
-  let clientDeferred = promise.defer();
-  let serverDeferred = promise.defer();
+  let clientDeferred = defer();
+  let serverDeferred = defer();
 
   let transport = yield transportFactory();
 
   let client = new DebuggerClient(transport);
   client.connect((app, traits) => {
     do_check_eq(traits.bulk, true);
     client.listTabs(clientDeferred.resolve);
   });
@@ -238,17 +238,17 @@ function verify_files() {
 
   let inputFile = getTestTempFile("bulk-input");
   let outputFile = getTestTempFile("bulk-output");
 
   do_check_eq(inputFile.fileSize, reallyLong.length);
   do_check_eq(outputFile.fileSize, reallyLong.length);
 
   // Ensure output file contents actually match
-  let compareDeferred = promise.defer();
+  let compareDeferred = defer();
   NetUtil.asyncFetch({
     uri: NetUtil.newURI(getTestTempFile("bulk-output")),
     loadUsingSystemPrincipal: true
   }, input => {
     let outputData = NetUtil.readInputStreamToString(input, reallyLong.length);
       // Avoid do_check_eq here so we don't log the contents
     do_check_true(outputData === reallyLong);
     input.close();
--- a/devtools/shared/transport/tests/unit/test_dbgsocket.js
+++ b/devtools/shared/transport/tests/unit/test_dbgsocket.js
@@ -40,17 +40,17 @@ function* test_socket_conn()
   do_check_eq(DebuggerServer.listeningSockets, 2);
 
   do_print("Starting long and unicode tests at " + new Date().toTimeString());
   let unicodeString = "(╯°□°)╯︵ ┻━┻";
   let transport = yield DebuggerClient.socketConnect({
     host: "127.0.0.1",
     port: gPort
   });
-  let closedDeferred = promise.defer();
+  let closedDeferred = defer();
   transport.hooks = {
     onPacket: function (aPacket) {
       this.onPacket = function (aPacket) {
         do_check_eq(aPacket.unicode, unicodeString);
         transport.close();
       };
       // Verify that things work correctly when bigger than the output
       // transport buffers and when transporting unicode...
--- a/devtools/shared/transport/tests/unit/test_dbgsocket_connection_drop.js
+++ b/devtools/shared/transport/tests/unit/test_dbgsocket_connection_drop.js
@@ -54,17 +54,17 @@ var test_helper = Task.async(function* (
   listener.portOrPath = -1;
   listener.authenticator = authenticator;
   listener.open();
 
   let transport = yield DebuggerClient.socketConnect({
     host: "127.0.0.1",
     port: listener.port
   });
-  let closedDeferred = promise.defer();
+  let closedDeferred = defer();
   transport.hooks = {
     onPacket: function (aPacket) {
       this.onPacket = function (aPacket) {
         do_throw(new Error("This connection should be dropped."));
         transport.close();
       };
 
       // Inject the payload directly into the stream.
--- a/devtools/shared/transport/tests/unit/test_no_bulk.js
+++ b/devtools/shared/transport/tests/unit/test_no_bulk.js
@@ -15,17 +15,17 @@ function run_test() {
   });
 
   run_next_test();
 }
 
 /** * Tests ***/
 
 var test_bulk_send_error = Task.async(function* (transportFactory) {
-  let deferred = promise.defer();
+  let deferred = defer();
   let transport = yield transportFactory();
 
   let client = new DebuggerClient(transport);
   return client.connect().then(([app, traits]) => {
     do_check_false(traits.bulk);
 
     try {
       client.startBulkRequest();
--- a/devtools/shared/transport/tests/unit/test_queue.js
+++ b/devtools/shared/transport/tests/unit/test_queue.js
@@ -20,18 +20,18 @@ function run_test() {
   });
 
   run_next_test();
 }
 
 /** * Tests ***/
 
 var test_transport = Task.async(function* (transportFactory) {
-  let clientDeferred = promise.defer();
-  let serverDeferred = promise.defer();
+  let clientDeferred = defer();
+  let serverDeferred = defer();
 
   // Ensure test files are not present from a failed run
   cleanup_files();
   let reallyLong = really_long();
   writeTestTempFile("bulk-input", reallyLong);
 
   do_check_eq(Object.keys(DebuggerServer._connections).length, 0);
 
@@ -144,17 +144,17 @@ function verify() {
 
   let inputFile = getTestTempFile("bulk-input");
   let outputFile = getTestTempFile("bulk-output");
 
   do_check_eq(inputFile.fileSize, reallyLong.length);
   do_check_eq(outputFile.fileSize, reallyLong.length);
 
   // Ensure output file contents actually match
-  let compareDeferred = promise.defer();
+  let compareDeferred = defer();
   NetUtil.asyncFetch({
     uri: NetUtil.newURI(getTestTempFile("bulk-output")),
     loadUsingSystemPrincipal: true
   }, input => {
     let outputData = NetUtil.readInputStreamToString(input, reallyLong.length);
       // Avoid do_check_eq here so we don't log the contents
     do_check_true(outputData === reallyLong);
     input.close();
--- a/devtools/shared/transport/tests/unit/test_transport_bulk.js
+++ b/devtools/shared/transport/tests/unit/test_transport_bulk.js
@@ -20,18 +20,18 @@ function run_test() {
 /** * Tests ***/
 
 /**
  * This tests a one-way bulk transfer at the transport layer.
  */
 var test_bulk_transfer_transport = Task.async(function* (transportFactory) {
   do_print("Starting bulk transfer test at " + new Date().toTimeString());
 
-  let clientDeferred = promise.defer();
-  let serverDeferred = promise.defer();
+  let clientDeferred = defer();
+  let serverDeferred = defer();
 
   // Ensure test files are not present from a failed run
   cleanup_files();
   let reallyLong = really_long();
   writeTestTempFile("bulk-input", reallyLong);
 
   do_check_eq(Object.keys(DebuggerServer._connections).length, 0);
 
@@ -115,17 +115,17 @@ function verify() {
 
   let inputFile = getTestTempFile("bulk-input");
   let outputFile = getTestTempFile("bulk-output");
 
   do_check_eq(inputFile.fileSize, reallyLong.length);
   do_check_eq(outputFile.fileSize, reallyLong.length);
 
   // Ensure output file contents actually match
-  let compareDeferred = promise.defer();
+  let compareDeferred = defer();
   NetUtil.asyncFetch({
     uri: NetUtil.newURI(getTestTempFile("bulk-output")),
     loadUsingSystemPrincipal: true
   }, input => {
     let outputData = NetUtil.readInputStreamToString(input, reallyLong.length);
       // Avoid do_check_eq here so we don't log the contents
     do_check_true(outputData === reallyLong);
     input.close();
--- a/devtools/shared/transport/transport.js
+++ b/devtools/shared/transport/transport.js
@@ -24,16 +24,17 @@
 }).call(this, function (require, exports) {
   const { Cc, Ci, Cr, CC } = require("chrome");
   const DevToolsUtils = require("devtools/shared/DevToolsUtils");
   const { dumpn, dumpv } = DevToolsUtils;
   const StreamUtils = require("devtools/shared/transport/stream-utils");
   const { Packet, JSONPacket, BulkPacket } =
   require("devtools/shared/transport/packets");
   const promise = require("promise");
+  const defer = require("devtools/shared/defer");
   const EventEmitter = require("devtools/shared/event-emitter");
 
   DevToolsUtils.defineLazyGetter(this, "Pipe", () => {
     return CC("@mozilla.org/pipe;1", "nsIPipe", "init");
   });
 
   DevToolsUtils.defineLazyGetter(this, "ScriptableInputStream", () => {
     return CC("@mozilla.org/scriptableinputstream;1",
@@ -595,17 +596,17 @@
 
       DevToolsUtils.executeSoon(DevToolsUtils.makeInfallible(() => {
         dumpn("Received bulk packet " + serial);
         if (!this.other.hooks) {
           return;
         }
 
         // Receiver
-        let deferred = promise.defer();
+        let deferred = defer();
         let packet = {
           actor: actor,
           type: type,
           length: length,
           copyTo: (output) => {
             let copying =
             StreamUtils.copyStream(pipe.inputStream, output, length);
             deferred.resolve(copying);
@@ -618,22 +619,22 @@
         this.other.emit("onBulkPacket", packet);
         this.other.hooks.onBulkPacket(packet);
 
         // Await the result of reading from the stream
         deferred.promise.then(() => pipe.inputStream.close(), this.close);
       }, "LocalDebuggerTransport instance's this.other.hooks.onBulkPacket"));
 
       // Sender
-      let sendDeferred = promise.defer();
+      let sendDeferred = defer();
 
       // The remote transport is not capable of resolving immediately here, so we
       // shouldn't be able to either.
       DevToolsUtils.executeSoon(() => {
-        let copyDeferred = promise.defer();
+        let copyDeferred = defer();
 
         sendDeferred.resolve({
           copyFrom: (input) => {
             let copying =
             StreamUtils.copyStream(input, pipe.outputStream, length);
             copyDeferred.resolve(copying);
             return copying;
           },
--- a/devtools/shared/webconsole/client.js
+++ b/devtools/shared/webconsole/client.js
@@ -4,16 +4,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 DevToolsUtils = require("devtools/shared/DevToolsUtils");
 const EventEmitter = require("devtools/shared/event-emitter");
 const promise = require("promise");
+const defer = require("devtools/shared/defer");
 const {LongStringClient} = require("devtools/shared/client/main");
 
 /**
  * A WebConsoleClient is used as a front end for the WebConsoleActor that is
  * created on the server, hiding implementation details.
  *
  * @param object debuggerClient
  *        The DebuggerClient instance we live for.
@@ -626,17 +627,17 @@ WebConsoleClient.prototype = {
       return promise.resolve(stringGrip);
     }
 
     // Fetch the long string only once.
     if (stringGrip._fullText) {
       return stringGrip._fullText.promise;
     }
 
-    let deferred = stringGrip._fullText = promise.defer();
+    let deferred = stringGrip._fullText = defer();
     let { initial, length } = stringGrip;
     let longStringClient = this.longString(stringGrip);
 
     longStringClient.substring(initial.length, length, response => {
       if (response.error) {
         DevToolsUtils.reportException("getString",
             response.error + ": " + response.message);