Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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 /**
6 * EventEmitter.
7 */
9 this.EventEmitter = function EventEmitter() {};
11 if (typeof(require) === "function") {
12 module.exports = EventEmitter;
13 var {Cu, components} = require("chrome");
14 } else {
15 var EXPORTED_SYMBOLS = ["EventEmitter"];
16 var Cu = this["Components"].utils;
17 var components = Components;
18 }
20 const { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
21 const { Services } = Cu.import("resource://gre/modules/Services.jsm");
23 /**
24 * Decorate an object with event emitter functionality.
25 *
26 * @param Object aObjectToDecorate
27 * Bind all public methods of EventEmitter to
28 * the aObjectToDecorate object.
29 */
30 EventEmitter.decorate = function EventEmitter_decorate (aObjectToDecorate) {
31 let emitter = new EventEmitter();
32 aObjectToDecorate.on = emitter.on.bind(emitter);
33 aObjectToDecorate.off = emitter.off.bind(emitter);
34 aObjectToDecorate.once = emitter.once.bind(emitter);
35 aObjectToDecorate.emit = emitter.emit.bind(emitter);
36 };
38 EventEmitter.prototype = {
39 /**
40 * Connect a listener.
41 *
42 * @param string aEvent
43 * The event name to which we're connecting.
44 * @param function aListener
45 * Called when the event is fired.
46 */
47 on: function EventEmitter_on(aEvent, aListener) {
48 if (!this._eventEmitterListeners)
49 this._eventEmitterListeners = new Map();
50 if (!this._eventEmitterListeners.has(aEvent)) {
51 this._eventEmitterListeners.set(aEvent, []);
52 }
53 this._eventEmitterListeners.get(aEvent).push(aListener);
54 },
56 /**
57 * Listen for the next time an event is fired.
58 *
59 * @param string aEvent
60 * The event name to which we're connecting.
61 * @param function aListener
62 * (Optional) Called when the event is fired. Will be called at most
63 * one time.
64 * @return promise
65 * A promise which is resolved when the event next happens. The
66 * resolution value of the promise is the first event argument. If
67 * you need access to second or subsequent event arguments (it's rare
68 * that this is needed) then use aListener
69 */
70 once: function EventEmitter_once(aEvent, aListener) {
71 let deferred = promise.defer();
73 let handler = function(aEvent, aFirstArg) {
74 this.off(aEvent, handler);
75 if (aListener) {
76 aListener.apply(null, arguments);
77 }
78 deferred.resolve(aFirstArg);
79 }.bind(this);
81 handler._originalListener = aListener;
82 this.on(aEvent, handler);
84 return deferred.promise;
85 },
87 /**
88 * Remove a previously-registered event listener. Works for events
89 * registered with either on or once.
90 *
91 * @param string aEvent
92 * The event name whose listener we're disconnecting.
93 * @param function aListener
94 * The listener to remove.
95 */
96 off: function EventEmitter_off(aEvent, aListener) {
97 if (!this._eventEmitterListeners)
98 return;
99 let listeners = this._eventEmitterListeners.get(aEvent);
100 if (listeners) {
101 this._eventEmitterListeners.set(aEvent, listeners.filter(l => {
102 return l !== aListener && l._originalListener !== aListener;
103 }));
104 }
105 },
107 /**
108 * Emit an event. All arguments to this method will
109 * be sent to listner functions.
110 */
111 emit: function EventEmitter_emit(aEvent) {
112 this.logEvent(aEvent, arguments);
114 if (!this._eventEmitterListeners || !this._eventEmitterListeners.has(aEvent)) {
115 return;
116 }
118 let originalListeners = this._eventEmitterListeners.get(aEvent);
119 for (let listener of this._eventEmitterListeners.get(aEvent)) {
120 // If the object was destroyed during event emission, stop
121 // emitting.
122 if (!this._eventEmitterListeners) {
123 break;
124 }
126 // If listeners were removed during emission, make sure the
127 // event handler we're going to fire wasn't removed.
128 if (originalListeners === this._eventEmitterListeners.get(aEvent) ||
129 this._eventEmitterListeners.get(aEvent).some(function(l) l === listener)) {
130 try {
131 listener.apply(null, arguments);
132 }
133 catch (ex) {
134 // Prevent a bad listener from interfering with the others.
135 let msg = ex + ": " + ex.stack;
136 Cu.reportError(msg);
137 dump(msg + "\n");
138 }
139 }
140 }
141 },
143 logEvent: function(aEvent, args) {
144 let logging = Services.prefs.getBoolPref("devtools.dump.emit");
146 if (logging) {
147 let caller = components.stack.caller.caller;
148 let func = caller.name;
149 let path = caller.filename.split(/ -> /)[1] + ":" + caller.lineNumber;
151 let argOut = "(";
152 if (args.length === 1) {
153 argOut += aEvent;
154 }
156 let out = "EMITTING: ";
158 // We need this try / catch to prevent any dead object errors.
159 try {
160 for (let i = 1; i < args.length; i++) {
161 if (i === 1) {
162 argOut = "(" + aEvent + ", ";
163 } else {
164 argOut += ", ";
165 }
167 let arg = args[i];
168 argOut += arg;
170 if (arg && arg.nodeName) {
171 argOut += " (" + arg.nodeName;
172 if (arg.id) {
173 argOut += "#" + arg.id;
174 }
175 if (arg.className) {
176 argOut += "." + arg.className;
177 }
178 argOut += ")";
179 }
180 }
181 } catch(e) {
182 // Object is dead so the toolbox is most likely shutting down,
183 // do nothing.
184 }
186 argOut += ")";
187 out += "emit" + argOut + " from " + func + "() -> " + path + "\n";
189 dump(out);
190 }
191 },
192 };