[Pkg-javascript-commits] [sockjs-client] 04/350: Fix handling of listeners being added and removed mid-dispatch
tonnerre at ancient-solutions.com
tonnerre at ancient-solutions.com
Fri Aug 5 01:02:56 UTC 2016
This is an automated email from the git hooks/post-receive script.
tonnerre-guest pushed a commit to branch upstream
in repository sockjs-client.
commit d2941069c5f1d5d6793ca65c9d4993b625171d44
Author: David Benjamin <davidben at mit.edu>
Date: Tue Jul 2 01:42:55 2013 -0400
Fix handling of listeners being added and removed mid-dispatch
Before, having the last event listener remove itself (to implement
EventEmitter.prototype.once-like semantics) threw an exception. Fix this by
making a copy of the listener list on dispatch. While we're at it, mimic the
semantics of the real DOM EventTarget interface. Event listeners added
mid-dispatch do not run until the next one, however listeners removed, if they
have not yet run, will not run in the current one.
Also adds a unit test to test this behavior.
---
lib/reventtarget.js | 32 ++++++++++++++++++++++++++------
tests/html/src/unittests.coffee | 23 ++++++++++++++++++++++-
2 files changed, 48 insertions(+), 7 deletions(-)
diff --git a/lib/reventtarget.js b/lib/reventtarget.js
index 48a798d..22c63b9 100644
--- a/lib/reventtarget.js
+++ b/lib/reventtarget.js
@@ -9,6 +9,15 @@
/* Simplified implementation of DOM2 EventTarget.
* http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-EventTarget
*/
+
+var indexOfListener = function(list, listener) {
+ for (var i = 0; i < list.length; i++) {
+ if (list[i].listener === listener)
+ return i;
+ }
+ return -1;
+};
+
var REventTarget = function() {};
REventTarget.prototype.addEventListener = function (eventType, listener) {
if(!this._listeners) {
@@ -18,8 +27,8 @@ REventTarget.prototype.addEventListener = function (eventType, listener) {
this._listeners[eventType] = [];
}
var arr = this._listeners[eventType];
- if(utils.arrIndexOf(arr, listener) === -1) {
- arr.push(listener);
+ if(indexOfListener(arr, listener) === -1) {
+ arr.push({listener: listener, doomed: false});
}
return;
};
@@ -29,10 +38,13 @@ REventTarget.prototype.removeEventListener = function (eventType, listener) {
return;
}
var arr = this._listeners[eventType];
- var idx = utils.arrIndexOf(arr, listener);
+ var idx = indexOfListener(arr, listener);
if (idx !== -1) {
+ // Removing an event listener that has not yet run
+ // mid-dispatch causes the listener to not run.
+ arr[idx].doomed = true;
if(arr.length > 1) {
- this._listeners[eventType] = arr.slice(0, idx).concat( arr.slice(idx+1) );
+ arr.splice(idx, 1);
} else {
delete this._listeners[eventType];
}
@@ -44,12 +56,20 @@ REventTarget.prototype.removeEventListener = function (eventType, listener) {
REventTarget.prototype.dispatchEvent = function (event) {
var t = event.type;
var args = Array.prototype.slice.call(arguments, 0);
+ // TODO: This doesn't match the real behavior; per spec, onfoo get
+ // their place in line from the /first/ time they're set from
+ // non-null. Although WebKit bumps it to the end every time it's
+ // set.
if (this['on'+t]) {
this['on'+t].apply(this, args);
}
if (this._listeners && t in this._listeners) {
- for(var i=0; i < this._listeners[t].length; i++) {
- this._listeners[t][i].apply(this, args);
+ // Make a copy of the listeners list; listeners added
+ // mid-dispatch should NOT run.
+ var listeners = this._listeners[t].slice(0);
+ for(var i=0; i < listeners.length; i++) {
+ if (!listeners[i].doomed)
+ listeners[i].listener.apply(this, args);
}
}
};
diff --git a/tests/html/src/unittests.coffee b/tests/html/src/unittests.coffee
index cf7b340..4931243 100644
--- a/tests/html/src/unittests.coffee
+++ b/tests/html/src/unittests.coffee
@@ -222,7 +222,7 @@ test 'detectProtocols', ->
['websocket', 'iframe-htmlfile', 'iframe-xhr-polling'])
test "EventEmitter", ->
- expect(4)
+ expect(6)
r = new SockJS('//1.2.3.4/wrongurl', null,
{protocols_whitelist: []})
r.addEventListener 'message', -> ok(true)
@@ -236,6 +236,27 @@ test "EventEmitter", ->
r.removeEventListener 'message', bluff
r.dispatchEvent({type:'message'})
+ # Listeners added mid-dispatch do not get run, however listeners
+ # removed mid-dispatch that have not yet run also don't run.
+ log = []
+ handler0 = -> log.push(0)
+ handler1 = ->
+ log.push(1)
+ r.removeEventListener 'test', handler0
+ r.removeEventListener 'test', handler2
+ r.addEventListener 'test', handler3
+ handler2 = -> log.push(2)
+ handler3 = -> log.push(3)
+ r.addEventListener 'test', handler0
+ r.addEventListener 'test', handler1
+ r.addEventListener 'test', handler2
+
+ r.dispatchEvent({type:'test'})
+ deepEqual(log, [0, 1])
+ log = []
+ r.dispatchEvent({type:'test'})
+ deepEqual(log, [1, 3])
+
# Adding the same eventlistener should be indempotent (sockjs-client #4).
single = -> ok(true)
r.addEventListener 'close', single
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/sockjs-client.git
More information about the Pkg-javascript-commits
mailing list