Bug 1273941 - replace uses of promise.defer in devtools/shared; r=jryans
authorTom Tromey <tom@tromey.com>
Thu, 09 Jun 2016 09:03:47 -0600
changeset 302529 51bc72f4ddcc7857749398cc8c9072587110858e
parent 302528 b3a0f1a9ede7f50e2da02d7efd8741da9a5c5937
child 302530 c93918e6463ac250d76f11f0e93aa76d9f740237
push id30364
push userkwierso@gmail.com
push dateFri, 24 Jun 2016 20:31:56 +0000
treeherdermozilla-central@9dac1358aaad [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjryans
bugs1273941
milestone50.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1273941 - replace uses of promise.defer in devtools/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);