toolkit/devtools/event-emitter.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

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 };

mercurial