Using indexeddb operation in multiple tabs - javascript

I am trying to save data from one tab to indexeddb and trying to get the data from another tab. But the operation is happening in second tab, only when i close the first tab or close the indexeddb in first tab using indexeddb.close();
How to get the data from indexeddb from other tabs(without closing the first tab or the indexeddb instance in the first tab)?

Data stored in indexedDB is available to all tabs from within the same origin. First clarify that both tabs point to the same origin.
However, indexedDB locks access to its data to essentially one tab at a time. If you have a connection to an indexedDB database open in one tab, and attempt to open a connection to the same database in a second tab, the second connection stalls, because it is blocked by the open connection in the other tab.
Notice that I say stalls, not fails. This is because the connection will eventually succeed (it becomes unblocked) once the tab that has the open connection either closes the connection or when the version change transaction completes (the onupgradeneeded handler function completes). The success event for the attempt to open the connection will eventually fire once it is unblocked.
The connection can be closed by calling IDBDatabase.prototype.close on the open connection, or by simply closing the tab.
One way to avoid some of the unwanted behavior and minimize how often a blocked event occurs is to avoid using a global database connection. By global I mean a database connection that you open some time near the time the tab is opened, when the dom is loaded and such, and then leave open for the remainder of the lifetime of the tab. Instead of opening a connection once for the entire lifetime of the tab, open a connection only when you need it, and then close it thereafter, and reopen a connection each additional time you need it, and close it thereafter. This way, the connection is closed for the majority of the time the tab is open, instead of being open for the majority of the time.
With all of that pretext, the answer to your question then is:
In first tab, open a connection, write data, then close connection.
Without closing first tab, then in second tab, open a connection, read data, then close connection.
Pay attention to the 'blocked' event. Add a listener in tab2 that listens for it. If you witness this event, then you tried to connect in tab2 while the tab1 connection was still open. Change your application to be able to react to this situation.
Here is simplistic example of listening for a blocked event:
function open(name, version, callback) {
var request = indexedDB.open(name, version);
request.onsuccess = function(event) {
var db = request.result;
callback(result, null);
};
request.onerror = function(event) {
console.log('Failed to connect');
callback(null, 'error');
};
request.onblocked = function(event) {
console.log('Failed to connect because blocked');
callback(null, 'blocked');
};
}
open('foo', 1, function onopen(db, error) {
if(error === 'error') {
console.log('cannot do stuff because of error');
} else if(error === 'blocked') {
console.log('cannot do stuff because blocked');
} else {
console.log('do stuff with db', db.name);
// do db operation here
// cleanup when done
db.close();
}
});

Related

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

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.

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

Multiple tabs same session, clear the session when all tabs are gone

So I recently had acceptance criteria for a site I was building that went as such:
After a user logs in to the site in any tab if they navigate to the site in a new tab they must already be logged in
When a user logs out of any tab they must log out of all tabs immediately
A user can refresh the page and stay logged in
Once all tabs are closed the user is logged out and must log back in
I didn't have access to change the server code (so this had to be done on the client)
I found this Question/Answer which was really helpful
When looking through this I had to rule out cookies because outside of doing a request to the server tab A will no know that tab B had changed the cookie
So I took some parts of the answer from the question above and started using local-storage and added an event to check for if the 'logged-in' state was changed which allowed me to log out in one tab and immediately log out in another without using setInterval to continuously check! Yay
But then I still had the issue of once all tabs were closed if you opened a new tab and navigated to the site you were still logged in.
I tried some possible solutions like having a counter of the tabs that has a session open, decrement and increment on tab close/open (using window.onbeforeunload). ISSUE: refresh of the site when there is only one tab active would log you out. Everything I could think of had an edge case where it didnt work.
local-storage + session-storage!
I would store the value logged-in in both the local-storage and the session storage, when a window was loaded (either a new tab or a refresh of the existing one) it would check local-storage for the 'logged-in' value and if it was not there it would check session-storage!
Basically I am using session-storage to handle the refresh of a page and local-storage to handle multiple tabs. Each time a window/tab is unloaded (closed or refreshed) I delete the local-storage 'logged-in' and when I come back into the page if it is in session-storage but not in local-storage I put it back into local-storage from the session-storage and continue as an authenticated user
Here is the code for this:
On login:
localStorage.setItem('logged-in', true);
sessionStorage.setItem('logged-in', true);
In my base component:
window.onbeforeunload = (event) => {
localStorage.removeItem('logged-in');
}
let loggedIn = localStorage.getItem('logged-in');
let sessionLoggedIn = sessionStorage.getItem('logged-in');
if(!loggedIn) {
if(sessionLoggedIn) {
localStorage.setItem('logged-in', JSON.parse(sessionLoggedIn));
//go to authenticated space
window.location.href = '/authenticated';
} else {
//go to login
window.location.href = '/login';
}
} else {
//go to authenticated space
window.location.href = '/authenticated';
}
window.addEventListener('storage', (event) => {
if (event.key == 'logout' && event.newValue) {
sessionStorage.removeItem('logged-in');
localStorage.removeItem('logout');
window.location.href = '/login';
}
});
On logout
localStorage.setItem('logout', true)
Hope this helps some of you if you ever find yourself in a similar situation

Chrome extension: detect headers after reload

I need to detect the request/response headers from my browser. I'm storing these headers in temporary variables that I will use later.
I want also to reset these variables when a page in a tab is reloaded.
I think I figured out the commands thanks to the guides and other people's answers. Anyway I saw that when I want to detect the reload of a page, the reload event seems to be fired after some of the request headers of the page are retrieved again.
Here's an example of what I get immediately after I refresh the page:
Here's my code:
/* Listener to the page for reloading */
chrome.tabs.onUpdated.addListener( function(tabId,changeInfo,tab){
//Check changeInfo.url: check existence - if it does, then that means that the url was changed and thus not a refresh.
console.log(changeInfo)
if (changeInfo.url === undefined) {
//reset the variables for a certain tab id on its refresh
if (changeInfo.status == 'loading') {
console.log('Refresh happened for tab: '+ tabId)
//global variables
requestHeaders = {}
responseHeaders = {}
}
}
});
/**
* Stores HTTP request headers into an object with TabId and URI as key.
*/
chrome.webRequest.onBeforeSendHeaders.addListener(
function(req){
var tabId = req.tabId;
/* header memorization */
console.log( tabId, req.url );
});
I realize these calls are asynchronous and to concatenate them I should put the call to onBeforeSendHeaders inside the callback of tabs.onUpdated, but in this case I'm losing (as in the case I reset the buffers) some of them because some of the headers seem to be received before the onUpdated event is fired.
How can I do to capture all the HTTP requests from when the page is loaded? That is, is there a way to attach the headers capturing function before the page starts receiving them?
Do not use the chrome.tabs event at all. Rendering (chrome.tabs) is completely unrelated to fetching the resource (chrome.webRequest).
Every set of requests for a tab starts with loading the top-level frame. You can detect this kind of request by checking whether req.type == 'main_frame'.
Note that your code only works for one tab. You should probably store the data in a dictionary using the tabId as key, and delete the value of the dictionary when the chrome.tabs.onRemoved event is triggered.

Test if socket is still open

Following instruction from my previous question, I now have an array of connected users in socket.io. My problem (which I was warned of in the answer) is that sockets stay in this array even after the browser has disconnected.
I tried removing sockets from the array in a socket.on('disconnect' function, but there is still a delay of ~1 minute between when the browser disconnects and socket.io triggers the disconnect.
What is the best way to "test" a socket to see if its actually alive? I am tempted to try to send a message and catch any errors, but I feel like there is a more elegant solution.
Solution on how to test if "socket is still open"
if(socket.readyState === socket.OPEN)
{
}
Why it works:
readyState = The current state of the connection; this is one of the Ready state
constants. Read only.
"the Ready state constants"
CONNECTING 0: The connection is not yet open.
OPEN 1: The connection is open and ready to communicate.
CLOSING 2: The connection is in theprocess of closing.
CLOSED 3: The connection is closed or couldn't be opened.
https://developer.mozilla.org/en-US/docs/Web/API/WebSocket
I had an error in my disconnect handler. What I ended up using:
socket.on('disconnect', function() {
users.splice(users.indexOf(socket), 1);
});
socket.on('end',function(){
//your code
})
or
socket.on('error',function(err){
//in case of any errors
})
The disconnect event wont fire until all the clients has been disconnected!

Categories

Resources