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