How to emit an event on application close using socket.io - javascript

I am trying emit an event while user closes the tab and for this I am emitting an event on unmount of a component ( which is nested inside a Main component) :
Emitting an event REMOVE_SOCKETS inside the cleanup function of useEffect unfortuantley doesn't trigger the event listener on the server side. I want to pass some paramertes like url, uid to know at which page/path the user closed the tab.
useEffect(() => {
return () => {
debugger;
if (socket) {
socket.emit('REMOVE_SOCKETS', {
url: window.location.href,
});
socket.disconnect();
}
};
}, []);
on express server side:
s.on('REMOVE_SOCKETS', () => {
// doing some application logic
})
I kept debugger statement to check if the dev tools in Chrome browser pauses instead of the closing of tab while unmounting the component but that didn't happen.
I searched in various answers of the similar question that to emit an event inside the disconnect event listner on server side as soon as we detect disconnect event like the following :
s.on('disconnect', () => {
s.emit('DISCONNECT_ACK_TO_CLIENT') // which is not needed in my case
} )
but in my case the client will no longer be available and also there is nothing I want to do by emitting event inside disconnect.
Also is there a way I can differetiate tab close and refresh, so that I can emit REMOVE_SOCEKTS only on tab close not on tab refresh ?

As far as I can tell, it is not possible to do much when the tab is being closed by the user. And this is on purpose. Otherwise, the user won't be able to close malicious tabs. Event if that was possible, it would still not be reliable - consider killing the browser using task manager for example.
One common solution would be to listen for the "close" event on the server side, and do whatever is needed. This should be complemented with ping/pong messages, in case the socket dies unexpectedly, to track the connection state.

Related

Service Worker: Inactive Event Listener

Does anybody know if there's an event listener which detects when a service worker is about to become inactive? Something like:
self.addEventListener('inactive', (event) =>
{
// Random piece of code here
}
Reason being is that I want to save data to storage before it becomes inactive since the data gets removed once it becomes inactive.

Are there any window events triggered if user "pulls the plug" and shuts down their computer?

I have a website, and I only want the client to be able to have 1 WebSocket connection at a time (when they open another tab while there is already another connection display, I display an error to them).
I'm working on a client-side solution where I update a flag in local storage to true when the connection is requested (It won't request if the flag is already true) then I listen for the beforeunload event and set the local storage flag to false if that tab had an open connection.
This seems to be working great except for the edge case of when a user shuts down their computer abruptly and thus beforeunload never fires, so when they turn their computer back on the local storage flag is stuck at true and they are stuck not being able to connect in any tabs.
Is there an event that will be called before the shutdown where I can set my local storage flag to false?
If not is there another solution for the client to keep track that it has only 1 WebSocket connection across all tabs so it can block a connection if there is already one?
window.addEventListener('beforeunload', this.setFlagToFalse);
As correctly stated in Jaromanda's comment, a computer without power can not emit an Event to the browser (which doesn't even exist anymore...).
However, one solution to your root problem is to listen to the storage event.
This event will fire across all the Windows that do share the same Storage area, when an other Window will make any modification to this Storage.
So we can use it as a mean to communicate between Windows from the same domain, in almost real time. This means that you don't have to keep your flag up to date, you can now know directly if an other Window is already active.
Here is a basic implementation. I'll let you the joy of making it more suited to your needs.
let alone = true; // a flag to know if we are alone
onstorage = e => { // listen to the storage event
if(e.key === 'am_I_alone') {
if(e.newValue === 'just checking') { // someone else is asking for permission
localStorage.am_I_alone = 'false'; // refuse
}
else if(e.newValue === 'false') { // we've been refused access
alone = false;
}
}
};
localStorage.am_I_alone = 'just checking'; // trigger the event on the other Windows
setTimeout(()=>{ // let them a little time to answer
if(alone) { // no response, we're good to go
// so the next one can trigger the event
localStorage.am_I_alone = "true";
startWebSocket();
}
else { // we've been rejected...
error();
}
}, 500);
Live Plnkr

PouchDB detect documents that aren't synced

I am trying to sync a local PouchDB instance to a remote CouchDB. Things work great, but I am not sure how to deal with the following situation:
I have added a validation rule in CouchDB to prevent updating (it will deny all updates). When I run the sync function on my local PouchDB instance after modifying a document, the "denied" event fires as I would expect. However, if I run sync a second time, the "denied" event doesn't fire again, even though the local document differs from the CouchDB version.
How can I check if the local database matches the remote database? If I miss the "denied" event the first time (lets say the user closes the browser), how can I detect on the next run that the databases are not in sync? How can I force PouchDB to try and sync the modified document again so that I can see the denied event?
Thanks!
syncPouch: function(){
var opts = {};
var sync = PouchDB.sync('orders', db.remoteDB, opts);
sync.on('change', function (info) {});
sync.on('paused', function(){
});
sync.on('active', function () {});
sync.on('denied', function(err){
//This only fire once no matter how many times I call syncPouch
console.log("Denied!!!!!!!!!!!!");
debugger;
});
sync.on('complete', function (info) {
//This fires every time
console.log("complete");console.log(info);
});
sync.on('error', function(err){
debugger;
});
return sync;
},
What I have noticed with validate_doc_update functions is that PouchDb appears to treat any "denied" document as sync-ed. So even if you then remove the validate_doc_update function, the document will not sync into the remote database on future attempts even though it is not the same.
So you can be left with an "out of sync" situation that can only be fixed by editing one of the documents again.
Perhaps you are seeing the same thing? Perhaps the "denied" event does not fire because there is no attempt by PouchDb to sync the document (as it has already attempted to sync it previously)?

How to properly handle chrome extension updates from content scripts

In background page we're able to detect extension updates using chrome.runtime.onInstalled.addListener.
But after extension has been updated all content scripts can't connect to the background page. And we get an error: Error connecting to extension ....
It's possible to re-inject content scripts using chrome.tabs.executeScript... But what if we have a sensitive data that should be saved before an update and used after update? What could we do?
Also if we re-inject all content scripts we should properly tear down previous content scripts.
What is the proper way to handle extension updates from content scripts without losing the user data?
If you've established a communication through var port = chrome.runtime.connect(...) (as described on
https://developer.chrome.com/extensions/messaging#connect), it should be possible to listen to the runtime.Port.onDisconnect event:
tport.onDisconnect.addListener(function(msg) {...})
There you can react and, e.g. apply some sort of memoization, let's say via localStorage. But in general, I would suggest to keep content scripts as tiny as possible and perform all the data manipulations in the background, letting content only to collect/pass data and render some state, if needed.
Once Chrome extension update happens, the "orphaned" content script is cut off from the extension completely. The only way it can still communicate is through shared DOM. If you're talking about really sensitive data, this is not secure from the page. More on that later.
First off, you can delay an update. In your background script, add a handler for the chrome.runtime.onUpdateAvailable event. As long as the listener is there, you have a chance to do cleanup.
// Background script
chrome.runtime.onUpdateAvailable.addListener(function(details) {
// Do your work, call the callback when done
syncRemainingData(function() {
chrome.runtime.reload();
});
});
Second, suppose the worst happens and you are cut off. You can still communicate using DOM events:
// Content script
// Get ready for data
window.addEventListener("SendRemainingData", function(evt) {
processData(evt.detail);
}, false);
// Request data
var event = new CustomEvent("RequestRemainingData");
window.dispatchEvent(event);
// Be ready to send data if asked later
window.addEventListener("RequestRemainingData", function(evt) {
var event = new CustomEvent("SendRemainingData", {detail: data});
window.dispatchEvent(event);
}, false);
However, this communication channel is potentially eavesdropped on by the host page. And, as said previously, that eavesdropping is not something you can bypass.
Yet, you can have some out-of-band pre-shared data. Suppose that you generate a random key on first install and keep it in chrome.storage - this is not accessible by web pages by any means. Of course, once orphaned you can't read it, but you can at the moment of injection.
var PSK;
chrome.storage.local.get("preSharedKey", function(data) {
PSK = data.preSharedKey;
// ...
window.addEventListener("SendRemainingData", function(evt) {
processData(decrypt(evt.detail, PSK));
}, false);
// ...
window.addEventListener("RequestRemainingData", function(evt) {
var event = new CustomEvent("SendRemainingData", {detail: encrypt(data, PSK)});
window.dispatchEvent(event);
}, false);
});
This is of course proof-of-concept code. I doubt that you will need more than an onUpdateAvailable listener.

Accessing every document that a user currently views from an extension

I'm writing an extension that checks every document a user views on certain data structures, does some back-end server calls and displays the results as a dialog.The problem is starting and continuing the sequence properly with event listeners. My actual idea is:
Load: function()
{
var Listener = function(){ Fabogore.Start();};
var ListenerTab = window.gBrowser.selectedTab;
ListenerTab.addEventListener("load",Listener,true);
}
(...)
ListenerTab.removeEventListener("load", Listener, true);
Fabogore.Load();
The Fabogore.Load function is first initialized when the browser gets opened. It works only once I get these data structures, but not afterwards. But theoretically the script should initialize a new listener, so maybe it's the selectedTab. I also tried listening to focus events.
If someone has got an alternative solution how to access a page a user is currently viewing I would feel comfortable as well.
The common approach is using a progress listener. If I understand correctly, you want to get a notification whenever a browser tab finished loading. So the important method in your progress listener would be onStateChange (it needs to have all the other methods as well however):
onStateChange: function(aWebProgress, aRequest, aFlag, aStatus)
{
if ((aFlag & Components.interfaces.nsIWebProgressListener.STATE_STOP) &&
(aFlag & Components.interfaces.nsIWebProgressListener.STATE_IS_WINDOW) &&
aWebProgress.DOMWindow == aWebProgress.DOMWindow.top)
{
// A window finished loading and it is the top-level frame in its tab
Fabogore.Start(aWebProgress.DOMWindow);
}
},
Ok, I found a way which works from the MDN documentation, and achieves that every document a user opens can be accessed by your extension. Accessing every document a user focuses is too much, I want the code to be executed only once. So I start with initializing the Exentsion, and Listen to DOMcontentloaded Event
window.addEventListener("load", function() { Fabogore.init(); }, false);
var Fabogore = {
init: function() {
var appcontent = document.getElementById("appcontent"); // browser
if(appcontent)
appcontent.addEventListener("DOMContentLoaded", Fabogore.onPageLoad, true);
},
This executes the code every Time a page is loaded. Now what's important is, that you execute your code with the new loaded page, and not with the old one. You can acces this one with the variable aEvent:
onPageLoad: function(aEvent)
{
var doc = aEvent.originalTarget;//Document which initiated the event
With the variable "doc" you can check data structures using XPCNativeWrapper etc. Thanks Wladimir for bringing me in the right direction, I suppose if you need a more sophisticated event listening choose his way with the progress listeners.

Categories

Resources