Bug 1384527 - Remove Promise.jsm usages from DevTools. r=jdescottes draft
authorAlexandre Poirot <poirot.alex@gmail.com>
Tue, 25 Jul 2017 15:56:17 +0200
changeset 615843 fcde17aa05c21b42fbb7fabe1927f8ce7f863b83
parent 614015 5928d905c0bc0b28f5488b236444c7d7991cf8d4
child 615844 58c18b66d03e7aa0154f738775c7d89a9eef9043
push id70493
push userbmo:poirot.alex@gmail.com
push dateWed, 26 Jul 2017 11:45:46 +0000
reviewersjdescottes
bugs1384527
milestone56.0a1
Bug 1384527 - Remove Promise.jsm usages from DevTools. r=jdescottes MozReview-Commit-ID: AZj1NXctSp8
devtools/shared/event-emitter.js
devtools/shared/shims/event-emitter.js
devtools/shared/worker/worker.js
--- a/devtools/shared/event-emitter.js
+++ b/devtools/shared/event-emitter.js
@@ -1,253 +1,196 @@
 /* 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";
 
-(function (factory) {
-  // This file can be loaded in several different ways.  It can be
-  // require()d, either from the main thread or from a worker thread;
-  // or it can be imported via Cu.import.  These different forms
-  // explain some of the hairiness of this code.
-  //
-  // It's important for the devtools-as-html project that a require()
-  // on the main thread not use any chrome privileged APIs.  Instead,
-  // the body of the main function can only require() (not Cu.import)
-  // modules that are available in the devtools content mode.  This,
-  // plus the lack of |console| in workers, results in some gyrations
-  // in the definition of |console|.
-  if (this.module && module.id.indexOf("event-emitter") >= 0) {
-    let console;
-    if (isWorker) {
-      console = {
-        error: () => {}
-      };
-    } else {
-      console = this.console;
+let EventEmitter = this.EventEmitter = function () {};
+module.exports = EventEmitter;
+
+// See comment in JSM module boilerplate when adding a new dependency.
+const Services = require("Services");
+const defer = require("devtools/shared/defer");
+const { describeNthCaller } = require("devtools/shared/platform/stack");
+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");
     }
-    // require
-    factory.call(this, require, exports, module, console);
-  } else {
-    // Cu.import.  This snippet implements a sort of miniature loader,
-    // which is responsible for appropriately translating require()
-    // requests from the client function.  This code can use
-    // Cu.import, because it is never run in the devtools-in-content
-    // mode.
-    this.isWorker = false;
-    const Cu = Components.utils;
-    let console = Cu.import("resource://gre/modules/Console.jsm", {}).console;
-    // 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) {
-      switch (module) {
-        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 "devtools/shared/platform/stack": {
-          let obj = {};
-          Cu.import("resource://devtools/shared/platform/chrome/stack.js", obj);
-          return obj;
-        }
+  });
+}
+
+/**
+ * Decorate an object with event emitter functionality.
+ *
+ * @param Object objectToDecorate
+ *        Bind all public methods of EventEmitter to
+ *        the objectToDecorate object.
+ * @return Object the object given.
+ */
+EventEmitter.decorate = function (objectToDecorate) {
+  let emitter = new EventEmitter();
+  objectToDecorate.on = emitter.on.bind(emitter);
+  objectToDecorate.off = emitter.off.bind(emitter);
+  objectToDecorate.once = emitter.once.bind(emitter);
+  objectToDecorate.emit = emitter.emit.bind(emitter);
+
+  return objectToDecorate;
+};
+
+EventEmitter.prototype = {
+  /**
+   * Connect a listener.
+   *
+   * @param string event
+   *        The event name to which we're connecting.
+   * @param function listener
+   *        Called when the event is fired.
+   */
+  on(event, listener) {
+    if (!this._eventEmitterListeners) {
+      this._eventEmitterListeners = new Map();
+    }
+    if (!this._eventEmitterListeners.has(event)) {
+      this._eventEmitterListeners.set(event, []);
+    }
+    this._eventEmitterListeners.get(event).push(listener);
+  },
+
+  /**
+   * Listen for the next time an event is fired.
+   *
+   * @param string event
+   *        The event name to which we're connecting.
+   * @param function listener
+   *        (Optional) Called when the event is fired. Will be called at most
+   *        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 = defer();
+
+    let handler = (_, first, ...rest) => {
+      this.off(event, handler);
+      if (listener) {
+        listener(event, first, ...rest);
       }
-      return null;
+      deferred.resolve(first);
     };
-    factory.call(this, require, this, { exports: this }, console);
-    this.EXPORTED_SYMBOLS = ["EventEmitter"];
-  }
-}).call(this, function (require, exports, module, console) {
-  // ⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠
-  // After this point the code may not use Cu.import, and should only
-  // require() modules that are "clean-for-content".
-  let EventEmitter = this.EventEmitter = function () {};
-  module.exports = EventEmitter;
 
-  // See comment in JSM module boilerplate when adding a new dependency.
-  const Services = require("Services");
-  const defer = require("devtools/shared/defer");
-  const { describeNthCaller } = require("devtools/shared/platform/stack");
-  let loggingEnabled = true;
+    handler._originalListener = listener;
+    this.on(event, handler);
 
-  if (!isWorker) {
-    loggingEnabled = Services.prefs.getBoolPref("devtools.dump.emit");
-    Services.prefs.addObserver("devtools.dump.emit", {
-      observe: () => {
-        loggingEnabled = Services.prefs.getBoolPref("devtools.dump.emit");
-      }
-    });
-  }
+    return deferred.promise;
+  },
 
   /**
-   * Decorate an object with event emitter functionality.
+   * Remove a previously-registered event listener.  Works for events
+   * registered with either on or once.
    *
-   * @param Object objectToDecorate
-   *        Bind all public methods of EventEmitter to
-   *        the objectToDecorate object.
-   * @return Object the object given.
+   * @param string event
+   *        The event name whose listener we're disconnecting.
+   * @param function listener
+   *        The listener to remove.
    */
-  EventEmitter.decorate = function (objectToDecorate) {
-    let emitter = new EventEmitter();
-    objectToDecorate.on = emitter.on.bind(emitter);
-    objectToDecorate.off = emitter.off.bind(emitter);
-    objectToDecorate.once = emitter.once.bind(emitter);
-    objectToDecorate.emit = emitter.emit.bind(emitter);
-
-    return objectToDecorate;
-  };
-
-  EventEmitter.prototype = {
-    /**
-     * Connect a listener.
-     *
-     * @param string event
-     *        The event name to which we're connecting.
-     * @param function listener
-     *        Called when the event is fired.
-     */
-    on(event, listener) {
-      if (!this._eventEmitterListeners) {
-        this._eventEmitterListeners = new Map();
-      }
-      if (!this._eventEmitterListeners.has(event)) {
-        this._eventEmitterListeners.set(event, []);
-      }
-      this._eventEmitterListeners.get(event).push(listener);
-    },
+  off(event, listener) {
+    if (!this._eventEmitterListeners) {
+      return;
+    }
+    let listeners = this._eventEmitterListeners.get(event);
+    if (listeners) {
+      this._eventEmitterListeners.set(event, listeners.filter(l => {
+        return l !== listener && l._originalListener !== listener;
+      }));
+    }
+  },
 
-    /**
-     * Listen for the next time an event is fired.
-     *
-     * @param string event
-     *        The event name to which we're connecting.
-     * @param function listener
-     *        (Optional) Called when the event is fired. Will be called at most
-     *        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 = defer();
-
-      let handler = (_, first, ...rest) => {
-        this.off(event, handler);
-        if (listener) {
-          listener(event, first, ...rest);
-        }
-        deferred.resolve(first);
-      };
-
-      handler._originalListener = listener;
-      this.on(event, handler);
-
-      return deferred.promise;
-    },
+  /**
+   * Emit an event.  All arguments to this method will
+   * be sent to listener functions.
+   */
+  emit(event) {
+    this.logEvent(event, arguments);
 
-    /**
-     * Remove a previously-registered event listener.  Works for events
-     * registered with either on or once.
-     *
-     * @param string event
-     *        The event name whose listener we're disconnecting.
-     * @param function listener
-     *        The listener to remove.
-     */
-    off(event, listener) {
+    if (!this._eventEmitterListeners || !this._eventEmitterListeners.has(event)) {
+      return;
+    }
+
+    let originalListeners = this._eventEmitterListeners.get(event);
+    for (let listener of this._eventEmitterListeners.get(event)) {
+      // If the object was destroyed during event emission, stop
+      // emitting.
       if (!this._eventEmitterListeners) {
-        return;
-      }
-      let listeners = this._eventEmitterListeners.get(event);
-      if (listeners) {
-        this._eventEmitterListeners.set(event, listeners.filter(l => {
-          return l !== listener && l._originalListener !== listener;
-        }));
-      }
-    },
-
-    /**
-     * Emit an event.  All arguments to this method will
-     * be sent to listener functions.
-     */
-    emit(event) {
-      this.logEvent(event, arguments);
-
-      if (!this._eventEmitterListeners || !this._eventEmitterListeners.has(event)) {
-        return;
+        break;
       }
 
-      let originalListeners = this._eventEmitterListeners.get(event);
-      for (let listener of this._eventEmitterListeners.get(event)) {
-        // If the object was destroyed during event emission, stop
-        // emitting.
-        if (!this._eventEmitterListeners) {
-          break;
-        }
-
-        // If listeners were removed during emission, make sure the
-        // event handler we're going to fire wasn't removed.
-        if (originalListeners === this._eventEmitterListeners.get(event) ||
-          this._eventEmitterListeners.get(event).some(l => l === listener)) {
-          try {
-            listener.apply(null, arguments);
-          } catch (ex) {
-            // Prevent a bad listener from interfering with the others.
-            let msg = ex + ": " + ex.stack;
-            console.error(msg);
-            dump(msg + "\n");
-          }
+      // If listeners were removed during emission, make sure the
+      // event handler we're going to fire wasn't removed.
+      if (originalListeners === this._eventEmitterListeners.get(event) ||
+        this._eventEmitterListeners.get(event).some(l => l === listener)) {
+        try {
+          listener.apply(null, arguments);
+        } catch (ex) {
+          // Prevent a bad listener from interfering with the others.
+          let msg = ex + ": " + ex.stack;
+          console.error(msg);
+          dump(msg + "\n");
         }
       }
-    },
+    }
+  },
 
-    logEvent(event, args) {
-      if (!loggingEnabled) {
-        return;
-      }
+  logEvent(event, args) {
+    if (!loggingEnabled) {
+      return;
+    }
 
-      let description = describeNthCaller(2);
+    let description = describeNthCaller(2);
 
-      let argOut = "(";
-      if (args.length === 1) {
-        argOut += event;
-      }
+    let argOut = "(";
+    if (args.length === 1) {
+      argOut += event;
+    }
 
-      let out = "EMITTING: ";
+    let out = "EMITTING: ";
 
-      // We need this try / catch to prevent any dead object errors.
-      try {
-        for (let i = 1; i < args.length; i++) {
-          if (i === 1) {
-            argOut = "(" + event + ", ";
-          } else {
-            argOut += ", ";
-          }
+    // We need this try / catch to prevent any dead object errors.
+    try {
+      for (let i = 1; i < args.length; i++) {
+        if (i === 1) {
+          argOut = "(" + event + ", ";
+        } else {
+          argOut += ", ";
+        }
 
-          let arg = args[i];
-          argOut += arg;
+        let arg = args[i];
+        argOut += arg;
 
-          if (arg && arg.nodeName) {
-            argOut += " (" + arg.nodeName;
-            if (arg.id) {
-              argOut += "#" + arg.id;
-            }
-            if (arg.className) {
-              argOut += "." + arg.className;
-            }
-            argOut += ")";
+        if (arg && arg.nodeName) {
+          argOut += " (" + arg.nodeName;
+          if (arg.id) {
+            argOut += "#" + arg.id;
+          }
+          if (arg.className) {
+            argOut += "." + arg.className;
           }
+          argOut += ")";
         }
-      } catch (e) {
-        // Object is dead so the toolbox is most likely shutting down,
-        // do nothing.
       }
+    } catch (e) {
+      // Object is dead so the toolbox is most likely shutting down,
+      // do nothing.
+    }
 
-      argOut += ")";
-      out += "emit" + argOut + " from " + description + "\n";
+    argOut += ")";
+    out += "emit" + argOut + " from " + description + "\n";
 
-      dump(out);
-    },
-  };
-});
+    dump(out);
+  },
+};
--- a/devtools/shared/shims/event-emitter.js
+++ b/devtools/shared/shims/event-emitter.js
@@ -15,17 +15,16 @@
     // require
     factory.call(this, require, exports, module);
   } else {
     // Cu.import
     const Cu = Components.utils;
     const { require } =
       Cu.import("resource://devtools/shared/Loader.jsm", {});
     this.isWorker = false;
-    this.promise = Cu.import("resource://gre/modules/Promise.jsm", {}).Promise;
     factory.call(this, require, this, { exports: this });
     this.EXPORTED_SYMBOLS = ["EventEmitter"];
   }
 }).call(this, function (require, exports, module) {
   const { Cu } = require("chrome");
   const Services = require("Services");
   const WARNING_PREF = "devtools.migration.warnings";
   // Cu and Services aren't accessible from workers
--- a/devtools/shared/worker/worker.js
+++ b/devtools/shared/worker/worker.js
@@ -12,17 +12,16 @@
     const { Cc, Ci, Cu, ChromeWorker } = require("chrome");
     const dumpn = require("devtools/shared/DevToolsUtils").dumpn;
     factory.call(this, require, exports, module, { Cc, Ci, Cu }, ChromeWorker, dumpn);
   } else {
     // Cu.import
     const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
     const { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
     this.isWorker = false;
-    this.Promise = Cu.import("resource://gre/modules/Promise.jsm", {}).Promise;
     this.console = Cu.import("resource://gre/modules/Console.jsm", {}).console;
     factory.call(
       this, require, this, { exports: this },
       { Cc, Ci, Cu }, ChromeWorker, null
     );
     this.EXPORTED_SYMBOLS = ["DevToolsWorker"];
   }
 }).call(this, function (require, exports, module, { Ci, Cc }, ChromeWorker, dumpn) {