devtools-connection
Advanced tools
Comparing version 0.0.10-rc7 to 0.0.11-rc1
{ | ||
"name": "devtools-connection", | ||
"version": "0.0.10-rc7", | ||
"version": "0.0.11-rc1", | ||
"description": "DevTools HTML Connection Logic", | ||
@@ -12,3 +12,4 @@ "main": "index.js", | ||
"license": "MPL-2.0", | ||
"dependencies": {} | ||
"dependencies": { | ||
} | ||
} |
@@ -5,257 +5,136 @@ /* This Source Code Form is subject to the terms of the Mozilla Public | ||
"use strict"; | ||
/** | ||
* EventEmitter. | ||
*/ | ||
const BAD_LISTENER = "The event listener must be a function, or an object that has " + | ||
"`EventEmitter.handler` Symbol."; | ||
var EventEmitter = function EventEmitter() {}; | ||
module.exports = EventEmitter; | ||
const eventListeners = Symbol("EventEmitter/listeners"); | ||
const onceOriginalListener = Symbol("EventEmitter/once-original-listener"); | ||
const handler = Symbol("EventEmitter/event-handler"); | ||
const promise = require("./promise"); | ||
const isEventHandler = (listener) => | ||
listener && handler in listener && typeof listener[handler] === "function"; | ||
/** | ||
* Decorate an object with event emitter functionality. | ||
* | ||
* @param Object aObjectToDecorate | ||
* Bind all public methods of EventEmitter to | ||
* the aObjectToDecorate object. | ||
*/ | ||
EventEmitter.decorate = function EventEmitter_decorate(aObjectToDecorate) { | ||
let emitter = new EventEmitter(); | ||
aObjectToDecorate.on = emitter.on.bind(emitter); | ||
aObjectToDecorate.off = emitter.off.bind(emitter); | ||
aObjectToDecorate.once = emitter.once.bind(emitter); | ||
aObjectToDecorate.emit = emitter.emit.bind(emitter); | ||
}; | ||
class EventEmitter { | ||
constructor() { | ||
this[eventListeners] = new Map(); | ||
} | ||
EventEmitter.prototype = { | ||
/** | ||
* Registers an event `listener` that is called every time events of | ||
* specified `type` is emitted on the given event `target`. | ||
* Connect a listener. | ||
* | ||
* @param {Object} target | ||
* Event target object. | ||
* @param {String} type | ||
* The type of event. | ||
* @param {Function|Object} listener | ||
* The listener that processes the event. | ||
* @param string aEvent | ||
* The event name to which we're connecting. | ||
* @param function aListener | ||
* Called when the event is fired. | ||
*/ | ||
static on(target, type, listener) { | ||
if (typeof listener !== "function" && !isEventHandler(listener)) { | ||
throw new Error(BAD_LISTENER); | ||
on: function EventEmitter_on(aEvent, aListener) { | ||
if (!this._eventEmitterListeners) { | ||
this._eventEmitterListeners = new Map(); | ||
} | ||
if (!(eventListeners in target)) { | ||
target[eventListeners] = new Map(); | ||
if (!this._eventEmitterListeners.has(aEvent)) { | ||
this._eventEmitterListeners.set(aEvent, []); | ||
} | ||
this._eventEmitterListeners.get(aEvent).push(aListener); | ||
}, | ||
const events = target[eventListeners]; | ||
if (events.has(type)) { | ||
events.get(type).add(listener); | ||
} else { | ||
events.set(type, new Set([listener])); | ||
} | ||
} | ||
/** | ||
* Removes an event `listener` for the given event `type` on the given event | ||
* `target`. If no `listener` is passed removes all listeners of the given | ||
* `type`. If `type` is not passed removes all the listeners of the given | ||
* event `target`. | ||
* @param {Object} target | ||
* The event target object. | ||
* @param {String} [type] | ||
* The type of event. | ||
* @param {Function|Object} [listener] | ||
* The listener that processes the event. | ||
* Listen for the next time an event is fired. | ||
* | ||
* @param string aEvent | ||
* The event name to which we're connecting. | ||
* @param function aListener | ||
* (Optional) Called when the event is fired. Will be called at most | ||
* one time. | ||
* @return promise | ||
* A promise which is resolved when the event next happens. The | ||
* resolution value of the promise is the first event argument. If | ||
* you need access to second or subsequent event arguments (it's rare | ||
* that this is needed) then use aListener | ||
*/ | ||
static off(target, type, listener) { | ||
const length = arguments.length; | ||
const events = target[eventListeners]; | ||
once: function EventEmitter_once(aEvent, aListener) { | ||
let deferred = promise.defer(); | ||
if (!events) { | ||
return; | ||
} | ||
let handler = (aEvent, aFirstArg, ...aRest) => { | ||
this.off(aEvent, handler); | ||
if (aListener) { | ||
aListener.apply(null, [aEvent, aFirstArg, ...aRest]); | ||
} | ||
deferred.resolve(aFirstArg); | ||
}; | ||
if (length === 3) { | ||
// Trying to remove from the `target` the `listener` specified for the | ||
// event's `type` given. | ||
const listenersForType = events.get(type); | ||
handler._originalListener = aListener; | ||
this.on(aEvent, handler); | ||
// If we don't have listeners for the event's type, we bail out. | ||
if (!listenersForType) { | ||
return; | ||
} | ||
return deferred.promise; | ||
}, | ||
// If the listeners list contains the listener given, we just remove it. | ||
if (listenersForType.has(listener)) { | ||
listenersForType.delete(listener); | ||
} else { | ||
// If it's not present, there is still the possibility that the listener | ||
// have been added using `once`, since the method wraps the original listener | ||
// in another function. | ||
// So we iterate all the listeners to check if any of them is a wrapper to | ||
// the `listener` given. | ||
for (const value of listenersForType.values()) { | ||
if (onceOriginalListener in value && value[onceOriginalListener] === listener) { | ||
listenersForType.delete(value); | ||
break; | ||
} | ||
} | ||
} | ||
} else if (length === 2) { | ||
// No listener was given, it means we're removing all the listeners from | ||
// the given event's `type`. | ||
if (events.has(type)) { | ||
events.delete(type); | ||
} | ||
} else if (length === 1) { | ||
// With only the `target` given, we're removing all the isteners from the object. | ||
events.clear(); | ||
} | ||
} | ||
/** | ||
* Registers an event `listener` that is called only the next time an event | ||
* of the specified `type` is emitted on the given event `target`. | ||
* It returns a promised resolved once the specified event `type` is emitted. | ||
* Remove a previously-registered event listener. Works for events | ||
* registered with either on or once. | ||
* | ||
* @param {Object} target | ||
* Event target object. | ||
* @param {String} type | ||
* The type of the event. | ||
* @param {Function|Object} [listener] | ||
* The listener that processes the event. | ||
* @return {Promise} | ||
* The promise resolved once the event `type` is emitted. | ||
* @param string aEvent | ||
* The event name whose listener we're disconnecting. | ||
* @param function aListener | ||
* The listener to remove. | ||
*/ | ||
static once(target, type, listener) { | ||
return new Promise(resolve => { | ||
// This is the actual listener that will be added to the target's listener, it wraps | ||
// the call to the original `listener` given. | ||
const newListener = (first, ...rest) => { | ||
// To prevent side effects we're removing the listener upfront. | ||
EventEmitter.off(target, type, newListener); | ||
if (listener) { | ||
if (isEventHandler(listener)) { | ||
// if the `listener` given is actually an object that handles the events | ||
// using `EventEmitter.handler`, we want to call that function, passing also | ||
// the event's type as first argument, and the `listener` (the object) as | ||
// contextual object. | ||
listener[handler](type, first, ...rest); | ||
} else { | ||
// Otherwise we'll just call it | ||
listener.call(target, first, ...rest); | ||
} | ||
} | ||
// We resolve the promise once the listener is called. | ||
resolve(first); | ||
}; | ||
newListener[onceOriginalListener] = listener; | ||
EventEmitter.on(target, type, newListener); | ||
}); | ||
} | ||
static emit(target, type, ...rest) { | ||
if (!(eventListeners in target)) { | ||
off: function EventEmitter_off(aEvent, aListener) { | ||
if (!this._eventEmitterListeners) { | ||
return; | ||
} | ||
if (target[eventListeners].has(type)) { | ||
// Creating a temporary Set with the original listeners, to avoiding side effects | ||
// in emit. | ||
const listenersForType = new Set(target[eventListeners].get(type)); | ||
for (const listener of listenersForType) { | ||
// If the object was destroyed during event emission, stop emitting. | ||
if (!(eventListeners in target)) { | ||
break; | ||
} | ||
const events = target[eventListeners]; | ||
const 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); | ||
} | ||
} catch (ex) { | ||
// Prevent a bad listener from interfering with the others. | ||
const msg = ex + ": " + ex.stack; | ||
console.error(msg); | ||
dump(msg + "\n"); | ||
} | ||
} | ||
} | ||
let listeners = this._eventEmitterListeners.get(aEvent); | ||
if (listeners) { | ||
this._eventEmitterListeners.set( | ||
aEvent, | ||
listeners.filter(l => { | ||
return l !== aListener && l._originalListener !== aListener; | ||
}), | ||
); | ||
} | ||
}, | ||
// 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. | ||
const 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. | ||
* @param {String} type | ||
* The type of event. | ||
* @return {Number} | ||
* The number of event listeners. | ||
* Emit an event. All arguments to this method will | ||
* be sent to listener functions. | ||
*/ | ||
static count(target, type) { | ||
if (eventListeners in target) { | ||
const listenersForType = target[eventListeners].get(type); | ||
emit: function EventEmitter_emit(aEvent) { | ||
if ( | ||
!this._eventEmitterListeners || !this._eventEmitterListeners.has(aEvent) | ||
) { | ||
return; | ||
} | ||
if (listenersForType) { | ||
return listenersForType.size; | ||
let originalListeners = this._eventEmitterListeners.get(aEvent); | ||
for (let listener of this._eventEmitterListeners.get(aEvent)) { | ||
// If the object was destroyed during event emission, stop | ||
// emitting. | ||
if (!this._eventEmitterListeners) { | ||
break; | ||
} | ||
// If listeners were removed during emission, make sure the | ||
// event handler we're going to fire wasn't removed. | ||
if ( | ||
originalListeners === this._eventEmitterListeners.get(aEvent) || | ||
this._eventEmitterListeners.get(aEvent).some(l => l === listener) | ||
) { | ||
try { | ||
listener.apply(null, arguments); | ||
} catch (ex) { | ||
// Prevent a bad listener from interfering with the others. | ||
let msg = `${ex }: ${ ex.stack}`; | ||
// console.error(msg); | ||
console.log(msg); | ||
} | ||
} | ||
} | ||
return 0; | ||
} | ||
/** | ||
* Decorate an object with event emitter functionality; basically using the | ||
* class' prototype as mixin. | ||
* | ||
* @param Object target | ||
* The object to decorate. | ||
* @return Object | ||
* The object given, mixed. | ||
*/ | ||
static decorate(target) { | ||
const descriptors = Object.getOwnPropertyDescriptors(this.prototype); | ||
delete descriptors.constructor; | ||
return Object.defineProperties(target, descriptors); | ||
} | ||
static get handler() { | ||
return handler; | ||
} | ||
on(...args) { | ||
EventEmitter.on(this, ...args); | ||
} | ||
off(...args) { | ||
EventEmitter.off(this, ...args); | ||
} | ||
once(...args) { | ||
return EventEmitter.once(this, ...args); | ||
} | ||
emit(...args) { | ||
EventEmitter.emit(this, ...args); | ||
} | ||
} | ||
module.exports = EventEmitter; | ||
}, | ||
}; |
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
300727
20
7060