I am trying to implement Server-Sent Events (SSE) inside a SharedWorker.
The implementation is working with no problems in Google Chrome. However, it does not work in FireFox at all.
When I try get it to work in FireFox I get this error in the console.
error {
target: SharedWorker,
isTrusted: true,
message: "ReferenceError: EventSource is not defined",
filename: "https://example.com/add-ons/icws/js/worker.js",
lineno: 28,
colno: 0,
currentTarget: SharedWorker,
eventPhase: 2,
bubbles: false,
cancelable: true,
defaultPrevented: false
}
How can I make EventSource available inside the SharedWorker?
This is how I establish connection the the SharedWorker
$(window).load(function(){
//establish connection to the shared worker
var worker = new SharedWorker("/add-ons/icws/js/worker.js" );
//listen for a message send from the worker
worker.port.addEventListener("message",
function(event) {
console.log( Math.random() );
processServerData(event.data);
}
, false
);
worker.onerror = function(event){
console.log(event);
};
//start the connection to the shared worker
worker.port.start();
});
This is my worker script
var clients = new Array();
readNewMessages();
//runs only when a new connection starts
onconnect = function(event) {
var port = event.ports[0];
clients.push(port);
port.start();
//implement a channel for a communication between the connecter and the SharedWorker
port.addEventListener("message",
function(event) {
replyToClientMessage(event, port);
} , false
);
}
//reply to any message sent to the SharedWorker
replyToClientMessage = function (event, port) {
port.postMessage(event.data);
}
//runs every time and post the message to all the connected client
function readNewMessages(){
var serv = new EventSource('/add-ons/icws/poll.php');
serv.addEventListener("getMessagingQueue", function(event) {
var queue = JSON.parse(event.data);
notifyAllPorts(queue);
}, false);
}
//check all open clients and post a message to each
function notifyAllPorts(msg){
var len = clients.length;
var port;
for(i = 0; i < len; i++) {
port = clients[i];
port.postMessage(msg);
}
}
While searching for a solution, I learned that EventSource is not part of SharedWorkerGlobalScope
I tried to change my worker code to this, but still that did not work
var serv = new self.EventSource('/add-ons/icws/poll.php');
var clients = new Array();
readNewMessages();
//runs only when a new connection starts
onconnect = function(event) {
var port = event.ports[0];
clients.push(port);
port.start();
//implement a channel for a communication between the connecter and the SharedWorker
port.addEventListener("message",
function(event) {
replyToClientMessage(event, port);
} , false
);
}
//reply to any message sent to the SharedWorker with the same message but add the phrase "SharedWorker Said: " to it
replyToClientMessage = function (event, port) {
port.postMessage(event.data);
}
//runs every time and post the message to all the connected client
function readNewMessages(){
serv.addEventListener("getMessagingQueue", function(event) {
var queue = JSON.parse(event.data);
notifyAllPorts(queue);
}, false);
}
//check all open clients and post a message to each
function notifyAllPorts(msg){
var len = clients.length;
var port;
for(i = 0; i < len; i++) {
port = clients[i];
port.postMessage(msg);
}
}
How can fix this problem?
Why FF would let you have a WebSocket in a Worker but not an EventSource I'm not sure, but it does give you all of the tools to make a good polyfill (stick it in the top of your SharedWorker script):
//FF only; some missing functionality, but handles the essentials
//most of what's missing can be added if you have the motivation
(function(global) {
if ('EventSource' in global)
return;
function EventSource(url) {
if (!(this instanceof EventSource))
return new EventSource(url);
this.url = url;
var self = this;
var listeners = {};
self.addEventListener = function(type, handler) {
if (!listeners[type]) {
listeners[type] = new Set();
}
listeners[type].add(handler);
};
self.removeEventListener = function(type, handler) {
if (listeners[type]) {
listeners[type].delete(handler);
}
};
self.dispatchEvent = function(event) {
if (listeners[event.type]) {
listeners[event.type].forEach(function(handler) {
setTimeout(function() {
switch (typeof(handler)) {
case 'object':
handler.handleEvent(event);
break;
case 'function':
handler(event);
break;
}
});
});
}
if (typeof(self['on' + event.type.toLowerCase()]) == 'function') {
setTimeout(function() {
self['on' + event.type.toLowerCase()](event);
});
}
};
var buffer = '';
//if you want to handle other prefixes, you'll need to tweak these
var msgRE = /^(?:data: .*\n)*\n/;
var dataRE = /^data: (.*)$/;
function _parse() {
while (msgRE.test(buffer)) {
var msg = msgRE.exec(buffer)[0]; //msg now contains a single raw message
var data = null;
var lines = msg.split("\n").slice(0, -2); //remove last 2 newlines
if (lines.length) {
data = '';
lines.forEach(function(line) {
data += dataRE.exec(line)[1];
});
}
var event = new MessageEvent('message', { 'data' : data, 'origin' : url });
self.dispatchEvent(event);
buffer = buffer.substr(msg.length);
}
}
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = 'moz-chunked-text'; //FF only
xhr.setRequestHeader('Accept', 'text/event-stream');
xhr.onprogress = function() {
if (xhr.response !== null) {
buffer += xhr.response;
}
_parse();
};
xhr.onreadystatechange = function() {
switch (xhr.readyState) {
case XMLHttpRequest.HEADERS_RECEIVED:
if (xhr.status == 200) {
self.readyState = EventSource.OPEN;
break;
} //else
console.error("EventSource: " + url + " = " + xhr.statusText);
//fallthrough
case XMLHttpRequest.DONE:
self.readyState = EventSource.CLOSED;
break;
default:
break;
}
};
xhr.send();
Object.defineProperty(this, 'close', { 'value' : function() {
xhr.abort();
}});
return this;
}
Object.defineProperties(EventSource, {
'CONNECTING' : { 'value' : 0, 'enumerable' : true },
'OPEN' : { 'value' : 1, 'enumerable' : true },
'CLOSED' : { 'value' : 2, 'enumerable' : true },
});
EventSource.prototype = Object.create(EventTarget.prototype);
Object.defineProperties(EventSource.prototype, {
'constructor' : { 'value' : EventSource },
'readyState' : { 'value' : 0, 'writable' : true, 'enumerable' : true },
'withCredentials' : { 'value' : false, 'enumerable' : true }, //not supported
'onopen' : { 'writable' : true },
'onmessage' : { 'writable' : true },
'onerror' : { 'writable' : true },
'close' : { 'value' : function() { }, 'configurable' : true, 'enumerable' : true }
});
global.EventSource = EventSource;
})(this);
You can find more complete polyfills here and here. I needed one that works with a real-time uncached stream (if you aren't connected to the stream when the event happens, it's gone); this is what I came up with. The main difference is the moz-chunked-text responseType, which gives you uncached stream access in the progress event. Enjoy ;-)
Related
I am trying to convert this whole section of code from Javascript to GopherJs.
So far i have not been able to do event listeners as i am still a newbie to Javascript.
This is the JavaScript
window.addEventListener("load", function(evt) {
var output = document.getElementById("output");
var input = document.getElementById("input");
var ws;
var print = function(message) {
var d = document.createElement("div");
d.innerHTML = message;
output.appendChild(d);
};
document.getElementById("open").onclick = function(evt) {
if (ws) {
return false;
}
ws = new WebSocket("{{.}}");
ws.onopen = function(evt) {
print("OPEN");
}
ws.onclose = function(evt) {
print("CLOSE");
ws = null;
}
ws.onmessage = function(evt) {
print("RESPONSE: " + evt.data);
}
ws.onerror = function(evt) {
print("ERROR: " + evt.data);
}
return false;
};
document.getElementById("send").onclick = function(evt) {
if (!ws) {
return false;
}
print("SEND: " + input.value);
ws.send(input.value);
return false;
};
document.getElementById("close").onclick = function(evt) {
if (!ws) {
return false;
}
ws.close();
return false;
};
});
I have gone through a few iterations of attempts but it is still not working.
Below is a snippet of my last attempt.
var ws *websocketjs.WebSocket
var err error
//js.Global.Get("document").Call("write", "Hello world!")
js.Global.Call("addEventListener", "load", func(ev *js.Object) {
//js.Global.Get("document").Get("open") = func(ev *js.Object){
onOpen := func(ev *js.Object) {
if ws == nil {
ws, err = websocketjs.New("ws://localhost:8000/ws") // Does not block.
if err != nil {
println(err)
}
}
fmt.Println("we are past the ws part")
js.Global.Get("document").Call("write", "It is opened!")
/////////////////////////////////////////////////
err = ws.Send("Hello Websockets!") // Send a text frame.
if err != nil {
fmt.Println(err)
}
println("it is open now")
}
ws.AddEventListener("open", false, onOpen)
//}
})
//ws.AddEventListener("open", false, onOpen)
//ws.AddEventListener("message", false, onMessage)
//ws.AddEventListener("close", false, onClose)
//ws.AddEventListener("error", false, onError)
err = ws.Close()
I would atleast like to see the first 2 parts done correctly. I can finish the rest with a good example.
Thanks
To obtain the DOM i used "honnef.co/go/js/dom" library.
Everything else was step by step as in Javascript.
Example:
package main
import "honnef.co/go/js/dom"
func main() {
d := dom.GetWindow().Document()
h := d.GetElementByID("foo")
h.SetInnerHTML("Hello World")
}
So I have this really weird issue which I did not find any solution online.
No matter what page I create, either .html or .php I get this Promises javascript in the head section. Has anyone seen this before or what is this?
// Promises
var _eid_promises = {};
// Turn the incoming message from extension
// into pending Promise resolving
window.addEventListener("message", function(event) {
if(event.source !== window) return;
if(event.data.src && (event.data.src === "background.js")) {
console.log("Page received: ");
console.log(event.data);
// Get the promise
if(event.data.nonce) {
var p = _eid_promises[event.data.nonce];
// resolve
if(event.data.result === "ok") {
if(event.data.signature !== undefined) {
p.resolve({hex: event.data.signature});
} else if(event.data.version !== undefined) {
p.resolve(event.data.extension + "/" + event.data.version);
} else if(event.data.cert !== undefined) {
p.resolve({hex: event.data.cert});
} else {
console.log("No idea how to handle message");
console.log(event.data);
}
} else {
// reject
p.reject(new Error(event.data.result));
}
delete _eid_promises[event.data.nonce];
} else {
console.log("No nonce in event msg");
}
}
}, false);
function TokenSigning() {
function nonce() {
var val = "";
var hex = "abcdefghijklmnopqrstuvwxyz0123456789";
for(var i = 0; i < 16; i++) val += hex.charAt(Math.floor(Math.random() * hex.length));
return val;
}
function messagePromise(msg) {
return new Promise(function(resolve, reject) {
// amend with necessary metadata
msg["nonce"] = nonce();
msg["src"] = "page.js";
// send message
window.postMessage(msg, "*");
// and store promise callbacks
_eid_promises[msg.nonce] = {
resolve: resolve,
reject: reject
};
});
}
this.getCertificate = function(options) {
var msg = {type: "CERT", lang: options.lang};
console.log("getCertificate()");
return messagePromise(msg);
};
this.sign = function(cert, hash, options) {
var msg = {type: "SIGN", cert: cert.hex, hash: hash.hex, hashtype: hash.type, lang: options.lang};
console.log("sign()");
return messagePromise(msg);
};
this.getVersion = function() {
console.log("getVersion()");
return messagePromise({
type: "VERSION"
});
};
}
This is Selenium extension.
Selenium Record and Playback tool for ease of getting acquainted with Selenium WebDriver.
extension id: mooikfkahbdckldjjndioackbalpho...
My issue is that once I open a page I get the data from the web-socket but while testing when I close my internet connection and then reconnect it, i see a message saying "Woops...Connection lost undefined". I want to reconnect to the web-socket automatically. My code is :
var socket = new SockJS("${createLink(uri: '/stomp')}");
var client = Stomp.over(socket);
client.connect({}, function () {
client.subscribe("/topic/${userInstance?.username}", function (message) {
console.log("Data on##....." + message.body);
var data = jQuery.parseJSON(message.body.toString());
var deviceId = $('#deviceId').find(":selected").val();
if (data.type == "balance") {
showDataBalance(data);
} else if (liveTrack == true && data.deviceId == deviceId && data.type == "event") {
var latLog = {
lat: parseFloat(data.eventData.latitude),
lng: parseFloat(data.eventData.longitude)
};
drawMarkerForLiveTracking(latLog, data.eventData);
}
});
});
And my STOMP.JS code is :
this.ws.onclose = function() {
var msg;
msg = "Whoops! Lost connection to " + _this.ws.url;
if (typeof _this.debug === "function") {
_this.debug(msg);
}
_this._cleanUp();
return typeof errorCallback === "function" ? errorCallback(msg) : void 0;
};
return this.ws.onopen = function() {
if (typeof _this.debug === "function") {
_this.debug('Web Socket Opened...');
}
headers["accept-version"] = Stomp.VERSIONS.supportedVersions();
headers["heart-beat"] = [_this.heartbeat.outgoing, _this.heartbeat.incoming].join(',');
return _this._transmit("CONNECT", headers);
};
What changes should I do in my connection establishment to make it auto re-connect?
here is my code. When my app is killed it and restarted on a push notification it should redirect properly however it never actually goes into the pushNotification.notificationCallback = function(event) Have no clue as to why.
function initPushWoosh() {
try {
checkNetworkConnection();
if(!window.plugins) {
wodifyRedirect('unknown-' + device.token);
return;
}
var pushNotification = window.plugins.pushNotification;
pushNotification.notificationCallback = function(event) {
var notificationId = 0;
if(event.u && event.u.custom) {
notificationId = event.u.custom;
} else if(event.u) {
notificationId = JSON.parse(event.u).custom;
} else if(event.custom && event.custom.custom) {
notificationId = event.custom.custom;
} else if(event.custom) {
notificationId = JSON.parse(event.custom).custom;
}
if(event.onStart && notificationId != 0) {
navigateToNotifications(notificationId, device.uuid);
}
};
Actually Pushwoosh defines its own notification callback:
PushNotification.prototype.notificationCallback = function(notification) {
var ev = document.createEvent('HTMLEvents');
ev.notification = notification;
ev.initEvent('push-notification', true, true, arguments);
document.dispatchEvent(ev);
};
this could be handled by:
document.addEventListener('push-notification', function(event) {
See Pushwoosh sample app here:
https://github.com/shaders/phonegap-3-sample-app/tree/master/www
I've managed to get websockets working inside a webworker using Chrome, but only for receiving data. When I try to send data I get a DOM Exception, has anyone managed to send data?
This is what I have for my web worker.
self.addEventListener('message', function(e) {
var data = e.data;
switch (data.cmd) {
case 'init':
self.postMessage("Initialising Web Workers...");
testWS();
break;
default:
self.postMessage('Unknown command: ' + data.msg);
};
}, false);
function testWS() {
var connectionAddr = "ws://localhost:8003";
var socket = new WebSocket(connectionAddr);
socket.onmessage = function(event) {
self.postMessage('Websocket : ' + event.data);
};
socket.onclose = function(event) {
};
function send(message) {
socket.send(message);
}
send("hello"); //Here is where the exception is thrown
}
You must listen for the onopen websocket event before sending your first message.
socket.onopen = function(){
// send some message
};
Try this:
var WebSocketStateEnum = {CONNECTING: 0, OPEN: 1, CLOSING: 2, CLOSED: 3};
var wsChannel;
var msgQueue = [];
// Using like this:
sendMessage(_ => {
wsChannel.send('message...'); // This will wait until the connection open, unless it is already opened
});
function sendMessage(task) {
if (!wsChannel || wsChannel.readyState != WebSocketStateEnum.OPEN) {
msgQueue.push(task);
} else {
task();
}
if (!wsChannel) {
wsChannel = new WebSocket('ws://your-url');
wsChannel.onopen = function() {
while (msgQueue.length > 0) {
msgQueue.shift()();
}
}
wsChannel.onmessage = function(evt) {
// message received here
}
wsChannel.onclose = function(evt) {
wsChannel = null;
}
wsChannel.onerror = function(evt) {
if (wsChannel.readyState == WebSocketStateEnum.OPEN) {
wsChannel.close();
} else {
wsChannel = null;
}
}
}
}