1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 export function EventEmitter() {}
7 let loggingEnabled = Services.prefs.getBoolPref("toolkit.dump.emit");
8 Services.prefs.addObserver("toolkit.dump.emit", {
10 loggingEnabled = Services.prefs.getBoolPref("toolkit.dump.emit");
15 * Decorate an object with event emitter functionality.
17 * @param Object objectToDecorate
18 * Bind all public methods of EventEmitter to
19 * the objectToDecorate object.
21 EventEmitter.decorate = function (objectToDecorate) {
22 let emitter = new EventEmitter();
23 objectToDecorate.on = emitter.on.bind(emitter);
24 objectToDecorate.off = emitter.off.bind(emitter);
25 objectToDecorate.once = emitter.once.bind(emitter);
26 objectToDecorate.emit = emitter.emit.bind(emitter);
29 function describeNthCaller(n) {
30 let caller = Components.stack;
31 // Do one extra iteration to skip this function.
34 caller = caller.caller;
37 let func = caller.name;
38 let file = caller.filename;
39 if (file.includes(" -> ")) {
40 file = caller.filename.split(/ -> /)[1];
42 let path = file + ":" + caller.lineNumber;
44 return func + "() -> " + path;
47 EventEmitter.prototype = {
52 * The event name to which we're connecting.
53 * @param function listener
54 * Called when the event is fired.
57 if (!this._eventEmitterListeners) {
58 this._eventEmitterListeners = new Map();
60 if (!this._eventEmitterListeners.has(event)) {
61 this._eventEmitterListeners.set(event, []);
63 this._eventEmitterListeners.get(event).push(listener);
67 * Listen for the next time an event is fired.
70 * The event name to which we're connecting.
71 * @param function listener
72 * (Optional) Called when the event is fired. Will be called at most
75 * A promise which is resolved when the event next happens. The
76 * resolution value of the promise is the first event argument. If
77 * you need access to second or subsequent event arguments (it's rare
78 * that this is needed) then use listener
80 once(event, listener) {
81 return new Promise(resolve => {
82 let handler = (_, first, ...rest) => {
83 this.off(event, handler);
85 listener(event, first, ...rest);
90 handler._originalListener = listener;
91 this.on(event, handler);
96 * Remove a previously-registered event listener. Works for events
97 * registered with either on or once.
100 * The event name whose listener we're disconnecting.
101 * @param function listener
102 * The listener to remove.
104 off(event, listener) {
105 if (!this._eventEmitterListeners) {
108 let listeners = this._eventEmitterListeners.get(event);
110 this._eventEmitterListeners.set(
112 listeners.filter(l => {
113 return l !== listener && l._originalListener !== listener;
120 * Emit an event. All arguments to this method will
121 * be sent to listener functions.
124 this.logEvent(event, arguments);
127 !this._eventEmitterListeners ||
128 !this._eventEmitterListeners.has(event)
133 let originalListeners = this._eventEmitterListeners.get(event);
134 for (let listener of this._eventEmitterListeners.get(event)) {
135 // If the object was destroyed during event emission, stop
137 if (!this._eventEmitterListeners) {
141 // If listeners were removed during emission, make sure the
142 // event handler we're going to fire wasn't removed.
144 originalListeners === this._eventEmitterListeners.get(event) ||
145 this._eventEmitterListeners.get(event).some(l => l === listener)
148 listener.apply(null, arguments);
156 logEvent(event, args) {
157 if (!loggingEnabled) {
161 let description = describeNthCaller(2);
164 if (args.length === 1) {
168 let out = "EMITTING: ";
170 // We need this try / catch to prevent any dead object errors.
172 for (let i = 1; i < args.length; i++) {
174 argOut = "(" + event + ", ";
182 if (arg && arg.nodeName) {
183 argOut += " (" + arg.nodeName;
185 argOut += "#" + arg.id;
188 argOut += "." + arg.className;
194 // Object is dead so the toolbox is most likely shutting down,
199 out += "emit" + argOut + " from " + description + "\n";