Bug 1137935 - add support for wildcard event type in devtools event emitter;r=ochameau draft
authorJulian Descottes <jdescottes@mozilla.com>
Thu, 17 Aug 2017 16:42:50 +0200
changeset 648296 4de2e6ec946634acaf638777aab2589eeb5125a2
parent 647355 9ab2470a3210324bc11320531b15d195aaf05051
child 648297 22d0003b40e0fc49e309187ea3c943a6f5048db5
push id74698
push userjdescottes@mozilla.com
push dateThu, 17 Aug 2017 15:14:34 +0000
reviewersochameau
bugs1137935
milestone57.0a1
Bug 1137935 - add support for wildcard event type in devtools event emitter;r=ochameau This API is only implemented as a temporary measure to ease the migration out of sdk APIs. MozReview-Commit-ID: BBlhxclpWqJ
devtools/shared/event-emitter.js
devtools/shared/tests/unit/test_eventemitter_basic.js
--- a/devtools/shared/event-emitter.js
+++ b/devtools/shared/event-emitter.js
@@ -145,50 +145,61 @@ class EventEmitter {
       newListener[onceOriginalListener] = listener;
       EventEmitter.on(target, type, newListener);
     });
   }
 
   static emit(target, type, ...rest) {
     logEvent(type, rest);
 
-    if (!(eventListeners in target) || !target[eventListeners].has(type)) {
+    if (!(eventListeners in target)) {
       return;
     }
 
-    // Creating a temporary Set with the original listeners, to avoiding side effects
-    // in emit.
-    let listenersForType = new Set(target[eventListeners].get(type));
+    if (target[eventListeners].has(type)) {
+      // Creating a temporary Set with the original listeners, to avoiding side effects
+      // in emit.
+      let listenersForType = new Set(target[eventListeners].get(type));
 
-    for (let listener of listenersForType) {
-      // If the object was destroyed during event emission, stop emitting.
-      if (!(eventListeners in target)) {
-        break;
-      }
+      for (let listener of listenersForType) {
+        // If the object was destroyed during event emission, stop emitting.
+        if (!(eventListeners in target)) {
+          break;
+        }
 
-      let events = target[eventListeners];
-      let listeners = events.get(type);
+        let events = target[eventListeners];
+        let listeners = events.get(type);
 
-      // If listeners were removed during emission, make sure the
-      // event handler we're going to fire wasn't removed.
-      if (listeners && listeners.has(listener)) {
-        try {
-          if (isEventHandler(listener)) {
-            listener[handler](type, ...rest);
-          } else {
-            listener.call(target, ...rest);
+        // If listeners were removed during emission, make sure the
+        // event handler we're going to fire wasn't removed.
+        if (listeners && listeners.has(listener)) {
+          try {
+            if (isEventHandler(listener)) {
+              listener[handler](type, ...rest);
+            } else {
+              listener.call(target, ...rest);
+            }
+          } catch (ex) {
+            // Prevent a bad listener from interfering with the others.
+            let msg = ex + ": " + ex.stack;
+            console.error(msg);
+            dump(msg + "\n");
           }
-        } catch (ex) {
-          // Prevent a bad listener from interfering with the others.
-          let msg = ex + ": " + ex.stack;
-          console.error(msg);
-          dump(msg + "\n");
         }
       }
     }
+
+    // Backward compatibility with the SDK event-emitter: support wildcard listeners that
+    // will be called for any event. The arguments passed to the listener are the event
+    // type followed by the actual arguments.
+    // !!! This API will be removed by Bug 1391261.
+    let hasWildcardListeners = target[eventListeners].has("*");
+    if (type !== "*" && hasWildcardListeners) {
+      EventEmitter.emit(target, "*", type, ...rest);
+    }
   }
 
   /**
    * Returns a number of event listeners registered for the given event `type`
    * on the given event `target`.
    *
    * @param {Object} target
    *    Event target object.
--- a/devtools/shared/tests/unit/test_eventemitter_basic.js
+++ b/devtools/shared/tests/unit/test_eventemitter_basic.js
@@ -175,16 +175,54 @@ const TESTS = {
 
     equal(secondCallbackCalled, false, "second callback not called yet");
 
     return Promise.all([ check1, check2, check3 ]).then(args => {
       equal(args[0], "rval from c1", "callback 1 done good");
       equal(args[1], "rval from c2", "callback 2 done good");
       equal(args[2], "rval from c3", "callback 3 done good");
     });
+  },
+
+  // This API is only provided for backward compatibility reasons with the old SDK
+  // event-emitter.
+  // !!! This API will be removed by Bug 1391261.
+  testWildcard() {
+    let emitter = getEventEmitter();
+
+    let received = [];
+    let listener = (...args) => received.push(args);
+
+    emitter.on("*", listener);
+
+    emitter.emit("a", 1);
+
+    equal(received.length, 1, "the listener was triggered once");
+    equal(received[0].length, 2, "the listener was called with 2 arguments");
+    equal(received[0][0], "a", "first argument is the event name");
+    equal(received[0][1], 1, "additional arguments are forwarded");
+
+    emitter.emit("*", "wildcard");
+
+    equal(received.length, 2, "the listener was only triggered once");
+    equal(received[1].length, 1, "the listener was called with only 1 argument");
+    equal(received[1][0], "wildcard", "first argument is the actual argument");
+
+    emitter.emit("other", "arg1", "arg2");
+
+    equal(received.length, 3, "the listener was triggered once");
+    equal(received[2].length, 3, "the listener was called with only 1 argument");
+    equal(received[2][0], "other", "first argument is the event name");
+    equal(received[2][1], "arg1", "additional arguments are forwarded");
+    equal(received[2][2], "arg2", "additional arguments are forwarded");
+
+    emitter.off("*", listener);
+    emitter.emit("a");
+    emitter.emit("*");
+    equal(received.length, 3, "the listener was not called anymore");
   }
 };
 
 /**
  * Create a runnable tests based on the tests descriptor given.
  *
  * @param {Object} tests
  *  The tests descriptor object, contains the tests to run.