MessageReceiver is an event target

Rather than asking for a global target, the message receiver implements
the EventTarget interface itself. It does not expose the dispatchEvent
method, however. This ensures that events can only be triggered from
within the internal MessageReceiver class, which means we no longer need
to namespace them.

// FREEBIE
This commit is contained in:
lilia 2015-09-01 12:13:38 -07:00
parent a925027cd2
commit 2243c09fea
3 changed files with 202 additions and 88 deletions

View file

@ -49,29 +49,33 @@
}
};
window.addEventListener('textsecure:message', onMessageReceived);
window.addEventListener('textsecure:receipt', onDeliveryReceipt);
window.addEventListener('textsecure:contact', onContactReceived);
window.addEventListener('textsecure:group', onGroupReceived);
window.addEventListener('textsecure:sent', onSentMessage);
window.addEventListener('textsecure:error', onError);
if (open) {
openInbox();
}
function init() {
window.removeEventListener('online', init);
if (!textsecure.registration.isDone()) { return; }
// initialize the socket and start listening for messages
messageReceiver = new textsecure.MessageReceiver(
'https://textsecure-service-staging.whispersystems.org',
textsecure.storage.get('number_id'),
textsecure.storage.get('password'),
textsecure.storage.get('signaling_key'),
window
);
if (messageReceiver) { messageReceiver.close(); }
var URL = 'https://textsecure-service-staging.whispersystems.org';
var USERNAME = storage.get('number_id');
var PASSWORD = storage.get('password');
var mySignalingKey = storage.get('signaling_key');
messageReceiver = new textsecure.MessageReceiver(URL, USERNAME, PASSWORD, mySignalingKey);
messageReceiver.addEventListener('message', onMessageReceived);
messageReceiver.addEventListener('receipt', onDeliveryReceipt);
messageReceiver.addEventListener('contact', onContactReceived);
messageReceiver.addEventListener('group', onGroupReceived);
messageReceiver.addEventListener('sent', onSentMessage);
messageReceiver.addEventListener('error', onError);
messageReceiver.addEventListener('contactsync', onContactSyncComplete);
}
function onContactSyncComplete() {
window.dispatchEvent(new Event('textsecure:contactsync'));
}
function onContactReceived(ev) {

View file

@ -39381,9 +39381,7 @@ function generateKeys(count, progressCallback) {
'use strict';
window.textsecure = window.textsecure || {};
function MessageReceiver(url, username, password, signalingKey, eventTarget) {
if (eventTarget instanceof EventTarget) {
this.target = eventTarget;
function MessageReceiver(url, username, password, signalingKey) {
this.url = url;
this.signalingKey = signalingKey;
this.username = username;
@ -39392,9 +39390,6 @@ function generateKeys(count, progressCallback) {
var unencoded = textsecure.utils.unencodeNumber(username);
this.number = unencoded[0];
this.deviceId = unencoded[1];
} else {
throw new TypeError('MessageReceiver expected an EventTarget');
}
this.connect();
}
@ -39403,7 +39398,7 @@ function generateKeys(count, progressCallback) {
connect: function() {
// initialize the socket and start listening for messages
if (this.socket && this.socket.readyState !== WebSocket.CLOSED) {
socket.close();
this.socket.close();
}
this.socket = new WebSocket(
this.url.replace('https://', 'wss://').replace('http://', 'ws://')
@ -39418,15 +39413,19 @@ function generateKeys(count, progressCallback) {
keepalive: { path: '/v1/keepalive', disconnect: true }
});
},
close: function() {
this.socket.close();
delete this.listeners;
},
onerror: function(error) {
console.log('websocket error', error);
this.socketError = error;
},
onclose: function(ev) {
var eventTarget = this.target;
var eventTarget = this;
console.log('websocket closed', ev.code);
// possible 403 or network issue. Make an request to confirm
TextSecureServer(this.url).getDevices(this.number).
TextSecureServer.getDevices(this.number).
then(this.connect.bind(this)). // No HTTP error? Reconnect
catch(function(e) {
var ev = new Event('textsecure:error');
@ -39458,9 +39457,9 @@ function generateKeys(count, progressCallback) {
}.bind(this)).catch(function(e) {
request.respond(500, 'Bad encrypted websocket message');
console.log("Error handling incoming message:", e);
var ev = new Event('textsecure:error');
var ev = new Event('error');
ev.error = e;
this.target.dispatchEvent(ev);
this.dispatchEvent(ev);
}.bind(this));
},
getStatus: function() {
@ -39471,9 +39470,9 @@ function generateKeys(count, progressCallback) {
}
},
onDeliveryReceipt: function (envelope) {
var ev = new Event('textsecure:receipt');
var ev = new Event('receipt');
ev.proto = envelope;
this.target.dispatchEvent(ev);
this.dispatchEvent(ev);
},
decrypt: function(envelope, ciphertext) {
return textsecure.protocol_wrapper.decrypt(
@ -39482,22 +39481,22 @@ function generateKeys(count, progressCallback) {
envelope.type,
ciphertext
).catch(function(error) {
var ev = new Event('textsecure:error');
var ev = new Event('error');
ev.error = error;
ev.proto = envelope;
this.target.dispatchEvent(ev);
this.dispatchEvent(ev);
throw error; // reject this promise
}.bind(this));
},
handleSentMessage: function(destination, timestamp, message) {
return processDecrypted(message, this.number).then(function(message) {
var ev = new Event('textsecure:sent');
var ev = new Event('sent');
ev.data = {
destination : destination,
timestamp : timestamp.toNumber(),
message : message
};
this.target.dispatchEvent(ev);
this.dispatchEvent(ev);
}.bind(this));
},
handleDataMessage: function(envelope, message, close_session) {
@ -39506,13 +39505,13 @@ function generateKeys(count, progressCallback) {
close_session();
}
return processDecrypted(message, envelope.source).then(function(message) {
var ev = new Event('textsecure:message');
var ev = new Event('message');
ev.data = {
source : envelope.source,
timestamp : envelope.timestamp.toNumber(),
message : message
};
this.target.dispatchEvent(ev);
this.dispatchEvent(ev);
}.bind(this));
},
handleLegacyMessage: function (envelope) {
@ -39560,22 +39559,22 @@ function generateKeys(count, progressCallback) {
}
},
handleContacts: function(contacts) {
var eventTarget = this.target;
var eventTarget = this;
var attachmentPointer = contacts.blob;
return handleAttachment(attachmentPointer).then(function() {
var contactBuffer = new ContactBuffer(attachmentPointer.data);
var contactDetails = contactBuffer.next();
while (contactDetails !== undefined) {
var ev = new Event('textsecure:contact');
var ev = new Event('contact');
ev.contactDetails = contactDetails;
eventTarget.dispatchEvent(ev);
contactDetails = contactBuffer.next();
}
eventTarget.dispatchEvent(new Event('textsecure:contactsync'));
eventTarget.dispatchEvent(new Event('contactsync'));
});
},
handleGroups: function(groups) {
var eventTarget = this.target;
var eventTarget = this;
var attachmentPointer = groups.blob;
return handleAttachment(attachmentPointer).then(function() {
var groupBuffer = new GroupBuffer(attachmentPointer.data);
@ -39595,7 +39594,7 @@ function generateKeys(count, progressCallback) {
);
}
}).then(function() {
var ev = new Event('textsecure:group');
var ev = new Event('group');
ev.groupDetails = groupDetails;
eventTarget.dispatchEvent(ev);
});
@ -39603,14 +39602,70 @@ function generateKeys(count, progressCallback) {
groupDetails = groupBuffer.next();
}
});
},
/* Implements EventTarget */
dispatchEvent: function(ev) {
if (!(ev instanceof Event)) {
throw new Error('Expects an event');
}
if (this.listeners === null || typeof this.listeners !== 'object') {
this.listeners = {};
}
var listeners = this.listeners[ev.type];
if (typeof listeners === 'object') {
for (var i=0; i < listeners.length; ++i) {
if (typeof listeners[i] === 'function') {
listeners[i].call(null, ev);
}
}
}
},
addEventListener: function(eventName, callback) {
if (typeof eventName !== 'string') {
throw new Error('First argument expects a string');
}
if (typeof callback !== 'function') {
throw new Error('Second argument expects a function');
}
if (this.listeners === null || typeof this.listeners !== 'object') {
this.listeners = {};
}
var listeners = this.listeners[eventName];
if (typeof listeners !== 'object') {
listeners = [];
}
listeners.push(callback);
this.listeners[eventName] = listeners;
},
removeEventListener: function(eventName, callback) {
if (typeof eventName !== 'string') {
throw new Error('First argument expects a string');
}
if (typeof callback !== 'function') {
throw new Error('Second argument expects a function');
}
if (this.listeners === null || typeof this.listeners !== 'object') {
this.listeners = {};
}
var listeners = this.listeners[eventName];
for (var i=0; i < listeners.length; ++ i) {
if (listeners[i] === callback) {
listeners.splice(i, 1);
return;
}
}
this.listeners[eventName] = listeners;
}
};
textsecure.MessageReceiver = function(url, username, password, signalingKey, eventTarget) {
var messageReceiver = new MessageReceiver(url, username, password, signalingKey, eventTarget);
this.getStatus = function() {
return messageReceiver.getStatus();
}
textsecure.MessageReceiver = function(url, username, password, signalingKey) {
var messageReceiver = new MessageReceiver(url, username, password, signalingKey);
this.addEventListener = messageReceiver.addEventListener.bind(messageReceiver);
this.removeEventListener = messageReceiver.removeEventListener.bind(messageReceiver);
this.getStatus = messageReceiver.getStatus.bind(messageReceiver);
}
textsecure.MessageReceiver.prototype = {

View file

@ -6,9 +6,7 @@
'use strict';
window.textsecure = window.textsecure || {};
function MessageReceiver(url, username, password, signalingKey, eventTarget) {
if (eventTarget instanceof EventTarget) {
this.target = eventTarget;
function MessageReceiver(url, username, password, signalingKey) {
this.url = url;
this.signalingKey = signalingKey;
this.username = username;
@ -17,9 +15,6 @@
var unencoded = textsecure.utils.unencodeNumber(username);
this.number = unencoded[0];
this.deviceId = unencoded[1];
} else {
throw new TypeError('MessageReceiver expected an EventTarget');
}
this.connect();
}
@ -28,7 +23,7 @@
connect: function() {
// initialize the socket and start listening for messages
if (this.socket && this.socket.readyState !== WebSocket.CLOSED) {
socket.close();
this.socket.close();
}
this.socket = new WebSocket(
this.url.replace('https://', 'wss://').replace('http://', 'ws://')
@ -43,15 +38,19 @@
keepalive: { path: '/v1/keepalive', disconnect: true }
});
},
close: function() {
this.socket.close();
delete this.listeners;
},
onerror: function(error) {
console.log('websocket error', error);
this.socketError = error;
},
onclose: function(ev) {
var eventTarget = this.target;
var eventTarget = this;
console.log('websocket closed', ev.code);
// possible 403 or network issue. Make an request to confirm
TextSecureServer(this.url).getDevices(this.number).
TextSecureServer.getDevices(this.number).
then(this.connect.bind(this)). // No HTTP error? Reconnect
catch(function(e) {
var ev = new Event('textsecure:error');
@ -83,9 +82,9 @@
}.bind(this)).catch(function(e) {
request.respond(500, 'Bad encrypted websocket message');
console.log("Error handling incoming message:", e);
var ev = new Event('textsecure:error');
var ev = new Event('error');
ev.error = e;
this.target.dispatchEvent(ev);
this.dispatchEvent(ev);
}.bind(this));
},
getStatus: function() {
@ -96,9 +95,9 @@
}
},
onDeliveryReceipt: function (envelope) {
var ev = new Event('textsecure:receipt');
var ev = new Event('receipt');
ev.proto = envelope;
this.target.dispatchEvent(ev);
this.dispatchEvent(ev);
},
decrypt: function(envelope, ciphertext) {
return textsecure.protocol_wrapper.decrypt(
@ -107,22 +106,22 @@
envelope.type,
ciphertext
).catch(function(error) {
var ev = new Event('textsecure:error');
var ev = new Event('error');
ev.error = error;
ev.proto = envelope;
this.target.dispatchEvent(ev);
this.dispatchEvent(ev);
throw error; // reject this promise
}.bind(this));
},
handleSentMessage: function(destination, timestamp, message) {
return processDecrypted(message, this.number).then(function(message) {
var ev = new Event('textsecure:sent');
var ev = new Event('sent');
ev.data = {
destination : destination,
timestamp : timestamp.toNumber(),
message : message
};
this.target.dispatchEvent(ev);
this.dispatchEvent(ev);
}.bind(this));
},
handleDataMessage: function(envelope, message, close_session) {
@ -131,13 +130,13 @@
close_session();
}
return processDecrypted(message, envelope.source).then(function(message) {
var ev = new Event('textsecure:message');
var ev = new Event('message');
ev.data = {
source : envelope.source,
timestamp : envelope.timestamp.toNumber(),
message : message
};
this.target.dispatchEvent(ev);
this.dispatchEvent(ev);
}.bind(this));
},
handleLegacyMessage: function (envelope) {
@ -185,22 +184,22 @@
}
},
handleContacts: function(contacts) {
var eventTarget = this.target;
var eventTarget = this;
var attachmentPointer = contacts.blob;
return handleAttachment(attachmentPointer).then(function() {
var contactBuffer = new ContactBuffer(attachmentPointer.data);
var contactDetails = contactBuffer.next();
while (contactDetails !== undefined) {
var ev = new Event('textsecure:contact');
var ev = new Event('contact');
ev.contactDetails = contactDetails;
eventTarget.dispatchEvent(ev);
contactDetails = contactBuffer.next();
}
eventTarget.dispatchEvent(new Event('textsecure:contactsync'));
eventTarget.dispatchEvent(new Event('contactsync'));
});
},
handleGroups: function(groups) {
var eventTarget = this.target;
var eventTarget = this;
var attachmentPointer = groups.blob;
return handleAttachment(attachmentPointer).then(function() {
var groupBuffer = new GroupBuffer(attachmentPointer.data);
@ -220,7 +219,7 @@
);
}
}).then(function() {
var ev = new Event('textsecure:group');
var ev = new Event('group');
ev.groupDetails = groupDetails;
eventTarget.dispatchEvent(ev);
});
@ -228,14 +227,70 @@
groupDetails = groupBuffer.next();
}
});
},
/* Implements EventTarget */
dispatchEvent: function(ev) {
if (!(ev instanceof Event)) {
throw new Error('Expects an event');
}
if (this.listeners === null || typeof this.listeners !== 'object') {
this.listeners = {};
}
var listeners = this.listeners[ev.type];
if (typeof listeners === 'object') {
for (var i=0; i < listeners.length; ++i) {
if (typeof listeners[i] === 'function') {
listeners[i].call(null, ev);
}
}
}
},
addEventListener: function(eventName, callback) {
if (typeof eventName !== 'string') {
throw new Error('First argument expects a string');
}
if (typeof callback !== 'function') {
throw new Error('Second argument expects a function');
}
if (this.listeners === null || typeof this.listeners !== 'object') {
this.listeners = {};
}
var listeners = this.listeners[eventName];
if (typeof listeners !== 'object') {
listeners = [];
}
listeners.push(callback);
this.listeners[eventName] = listeners;
},
removeEventListener: function(eventName, callback) {
if (typeof eventName !== 'string') {
throw new Error('First argument expects a string');
}
if (typeof callback !== 'function') {
throw new Error('Second argument expects a function');
}
if (this.listeners === null || typeof this.listeners !== 'object') {
this.listeners = {};
}
var listeners = this.listeners[eventName];
for (var i=0; i < listeners.length; ++ i) {
if (listeners[i] === callback) {
listeners.splice(i, 1);
return;
}
}
this.listeners[eventName] = listeners;
}
};
textsecure.MessageReceiver = function(url, username, password, signalingKey, eventTarget) {
var messageReceiver = new MessageReceiver(url, username, password, signalingKey, eventTarget);
this.getStatus = function() {
return messageReceiver.getStatus();
}
textsecure.MessageReceiver = function(url, username, password, signalingKey) {
var messageReceiver = new MessageReceiver(url, username, password, signalingKey);
this.addEventListener = messageReceiver.addEventListener.bind(messageReceiver);
this.removeEventListener = messageReceiver.removeEventListener.bind(messageReceiver);
this.getStatus = messageReceiver.getStatus.bind(messageReceiver);
}
textsecure.MessageReceiver.prototype = {