Exchange Information between two windows - javascript

Im not sure how to explain my problem in short at the title, so forgive me for this. Lets say I opened my website in some random page, and I have a register button. Really simple, but in my case I require the registration form to appear in other tab(or window). Not "RederPartial", nor popup window of some kind, legit new tab. The problem is, I want to be able do detect, from that random page of mine, when the registration completed (ie a new user has been created). How can I pass this information? without reloading the first page

There are various ways to make two windows talk with each other (through server, with cookies, using FileSystem API or Local Storage.
Locale Storage is by far the easiest way to talk between two windows who come from the same domain, but it is not supported in older browsers. Since you need to contact the server anyway to find out when someone has registered, I recommend using the server (Ajax//web sockets).
Here's a somewhat simple AJAX solution which you could use in your random page:
(function(){
var formOpened = false;
var registered = false;
//Change to whatever interaction is needed to open the form
$('#registerbutton').on('click', function(){
if (!formOpened){
formOpened = true;
//Will try to poll the server every 3 seconds
(function pollServer(){
$.ajax({
url: "/ajax/pollregistered",
type: "GET",
success: function(data) {
if (data.registered){
registered = true;
//do other stuff here that is needed after registration
}
},
dataType: "json",
complete: setTimeout(function() {if (!registered) pollServer();}, 3000),
//will timeout if request takes longer than 2 seconds
timeout: 2000,
})
})();
//opens the new tab
var win = window.open('/registrationform', '_blank');
win.focus();
}
});
})();
You can then use the session key of the visitor in the '/ajax/pollregistered' action to write your backend code to check if and when this visitor registered as user to your site.
If you don't mind using the local storage, you could poll the storage instead of the server and have the registration page write to local storage upon registration. This is less stressing for your server, but might not work for users with ancient browsers.
Also worth nothing that this method of opening a new tab is not completely reliable. Technically there is not a sure shot way of opening a new tab with javascript. It depends on browser and the preferences of the user. Opening a new tab might get blocked for users who are using anti popup plugins.
That being said, I strongly recommend revising your design and try finding a way to work with just one window instead. It's probably better for both yourself and your users.

Related

keep session after login - selenium - javascript

I am trying to automate couple of pages using selenium web driver and node js . I was able to login , but after login I want to use same session initiated by web driver so that I can do automated testing on session protected page. This is my attempt
async function login(){
Let d = await new Builder()
.forBrowser('chrome')
.build();
await d.get('https://demo.textdomain.com/')
await d.findElement(By.id('username')).sendKeys('admin ')
await d.findElement(By.id('password')).sendKeys('admin');
await d.findElement(By.css('button[type="submit"]')).click();
d.getPageSource().then(function(content) {
if(content.indexOf('Welcome text') !==-1 ) {
console.log('Test passed');
console.log('landing page');
d.get('https://demo.textdomain.com/landingpage') //this is still going to login page as i cannot use the previous session
} else {
console.log('Test failed');
return false;
}
//driver.quit();
});
}
login();
Am I accidentally discarding the browser after login.
From a similar question on SQA StackExchange, you can store and restore the current session's cookies:
Using Javascript:
// Storing cookies:
driver.manage().getCookies().then(function (cookies) {
allCookies = cookies;
});
// Restoring cookies:
for (var key in allCookies) {
driver.manage().addCookie(key, allCookies[key]);
}
You might just be dealing with timing issues. Selenium moves very fast. Way faster than you can interact as a user. So it often acts in what seems like unpredictable ways. But that's only because Selenium is acting much faster than you would as a user. In order to work around this, you should make good use of Selenium's built-in driver.wait. For example:
const button = driver.wait(
until.elementLocated(By.id('my-button')),
20000
);
button.click();
The above waits until the button with id my-button is present in the DOM, and then clicks it. It will wait for a maximum of 20000 milliseconds, but will finish as soon as the button becomes available.
So in your case, if there is something that becomes available after the user is successfully logged in, you could wait on that element before going to the new page in your code.
As an aside, I'm also not so sure why you are using getPageSource()? That seems like a very heavy-handed way to get what you are looking for. Isn't that content inside an element you could get the contents of?
I wrote an article about How to write reliable browser tests using Selenium and Node.js which might help you understand in more detail the code example above from the article, along with other techniques you can use to wait reliably for a variety of conditions in the browser.
I believe your problem is not properly waiting for the login to complete.
Selenium doesn't wait for asynchronous actions to be done, it moves to the next line, so when you ask for the page source, there is a good chance the login action didn't complete on the server and the result is not what you expect it to be.
you have to explicitly tell Selenium to wait, so you need to add some code between the login and the code that checks if the user is login, for the sake of this assumption, add a 10 seconds timeout.
if this works for you, you wouldn't want to just waste time, so you need to wait for certain elements on the page to change because of the login, for example, you need to wait for the presence (or visibility if it is already in the DOM) of the user photo in the header.
also, I'm not sure how the "getPageSource" function behaves, it can use the existing page, or it can ask for a fresh copy.
I would advise you to use other ways to test if the user is logged in, by inspecting the DOM.
I suggest to re-use the session-cookie after first login in other web-driver instances.
First store the cookie:
var cookieValue = firstWebDriver.Manage().Cookies.GetCookieNamed(name:"cookie_name");
Then you can pass it by to any WebDriver instance, set it and drive the web-app as it would be the same user with different browser instances:
anotherWebDriver.Manage().Cookies.AddCookie(new Cookie(name:"cookie_name", value:cookieValue));
If you want to use the same browser instance, you have to synchronize them, because WebDriver invocations are in general not thread-safe and would probalby often lead to exceptions (e.g. stale because an element was changed or notfound, because one web-driver navigated to a different page).
Then I suggest to just use the window handle for the next instance, without caring about the session. The first one opens and the last one closes the session (count the referenced handles) and be sure only one driver uses this handle at a time. You can also create new browser windows and this will keep the session and give you a new handle:
var handle = firstWebDriver.CurrentWindowHandle;
otherWebDriver.SwitchTo().Window(handle);
I wrote the code in C# but should be easily adaptable to JavaScript.

Run a bash script through CGI on closing of browser window [duplicate]

I'm trying to find out when a user left a specified page. There is no problem finding out when he used a link inside the page to navigate away but I kind of need to mark up something like when he closed the window or typed another URL and pressed enter. The second one is not so important but the first one is. So here is the question:
How can I see when a user closed my page (capture window.close event), and then... doesn't really matter (I need to send an AJAX request, but if I can get it to run an alert, I can do the rest).
Updated 2021
TL;DR
Beacon API is the solution to this issue (on almost every browser).
A beacon request is supposed to complete even if the user exits the page.
When should you trigger your Beacon request ?
This will depend on your usecase. If you are looking to catch any user exit, visibilitychange (not unload) is the last event reliably observable by developers in modern browsers.
NB: As long as implementation of visibilitychange is not consistent across browsers, you can detect it via the lifecycle.js library.
# lifecycle.js (1K) for cross-browser compatibility
# https://github.com/GoogleChromeLabs/page-lifecycle
<script defer src="/path/to/lifecycle.js"></script>
<script defer>
lifecycle.addEventListener('statechange', function(event) {
if (event.originalEvent == 'visibilitychange' && event.newState == 'hidden') {
var url = "https://example.com/foo";
var data = "bar";
navigator.sendBeacon(url, data);
}
});
</script>
Details
Beacon requests are supposed to run to completion even if the user leaves the page - switches to another app, etc - without blocking user workflow.
Under the hood, it sends a POST request along with the user credentials (cookies), subject to CORS restrictions.
var url = "https://example.com/foo";
var data = "bar";
navigator.sendBeacon(url, data);
The question is when to send your Beacon request. Especially if you want to wait until the last moment to send session info, app state, analytics, etc.
It used to be common practice to send it during the unload event, but changes to page lifecycle management - driven by mobile UX - killed this approach. Today, most mobile workflows (switching to new tab, switching to the homescreen, switching to another app...) do not trigger the unload event.
If you want to do things when a user exits your app/page, it is now recommended to use the visibilitychange event and check for transitioning from passive to hidden state.
document.addEventListener('visibilitychange', function() {
if (document.visibilityState == 'hidden') {
// send beacon request
}
});
The transition to hidden is often the last state change that's reliably observable by developers (this is especially true on mobile, as users can close tabs or the browser app itself, and the beforeunload, pagehide, and unload events are not fired in those cases).
This means you should treat the hidden state as the likely end to the user's session. In other words, persist any unsaved application state and send any unsent analytics data.
Details of the Page lifecyle API are explained in this article.
However, implementation of the visibilitychange event, as well as the Page lifecycle API is not consistent across browsers.
Until browser implementation catches up, using the lifecycle.js library and page lifecycle best practices seems like a good solution.
# lifecycle.js (1K) for cross-browser compatibility
# https://github.com/GoogleChromeLabs/page-lifecycle
<script defer src="/path/to/lifecycle.js"></script>
<script defer>
lifecycle.addEventListener('statechange', function(event) {
if (event.originalEvent == 'visibilitychange' && event.newState == 'hidden') {
var url = "https://example.com/foo";
var data = "bar";
navigator.sendBeacon(url, data);
}
});
</script>
For more numbers about the reliability of vanilla page lifecycle events (without lifecycle.js), there is also this study.
Adblockers
Adblockers seem to have options that block sendBeacon requests.
Cross site requests
Beacon requests are POST requests that include cookies and are subject to CORS spec. More info.
There are unload and beforeunload javascript events, but these are not reliable for an Ajax request (it is not guaranteed that a request initiated in one of these events will reach the server).
Therefore, doing this is highly not recommended, and you should look for an alternative.
If you definitely need this, consider a "ping"-style solution. Send a request every minute basically telling the server "I'm still here". Then, if the server doesn't receive such a request for more than two minutes (you have to take into account latencies etc.), you consider the client offline.
Another solution would be to use unload or beforeunload to do a Sjax request (Synchronous JavaScript And XML), but this is completely not recommended. Doing this will basically freeze the user's browser until the request is complete, which they will not like (even if the request takes little time).
1) If you're looking for a way to work in all browsers, then the safest way is to send a synchronous AJAX to the server. It is is not a good method, but at least make sure that you are not sending too much of data to the server, and the server is fast.
2) You can also use an asynchronous AJAX request, and use ignore_user_abort function on the server (if you're using PHP). However ignore_user_abort depends a lot on server configuration. Make sure you test it well.
3) For modern browsers you should not send an AJAX request. You should use the new navigator.sendBeacon method to send data to the server asynchronously, and without blocking the loading of the next page. Since you're wanting to send data to server before user moves out of the page, you can use this method in a unload event handler.
$(window).on('unload', function() {
var fd = new FormData();
fd.append('ajax_data', 22);
navigator.sendBeacon('ajax.php', fd);
});
There also seems to be a polyfill for sendBeacon. It resorts to sending a synchronous AJAX if method is not natively available.
IMPORTANT FOR MOBILE DEVICES : Please note that unload event handler is not guaranteed to be fired for mobiles. But the visibilitychange event is guaranteed to be fired. So for mobile devices, your data collection code may need a bit of tweaking.
You may refer to my blog article for the code implementation of all the 3 ways.
I also wanted to achieve the same functionality & came across this answer from Felix(it is not guaranteed that a request initiated in one of these events will reach the server).
To make the request reach to the server we tried below code:-
onbeforeunload = function() {
//Your code goes here.
return "";
}
We are using IE browser & now when user closes the browser then he gets the confirmation dialogue because of return ""; & waits for user's confirmation & this waiting time makes the request to reach the server.
Years after posting the question I made a way better implementation including nodejs and socket.io (https://socket.io) (you can use any kind of socket for that matter but that was my personal choice).
Basically I open up a connection with the client, and when it hangs up I just save data / do whatever I need. Obviously this cannot be use to show anything / redirect the client (since you are doing it server side), but is what I actually needed back then.
io.on('connection', function(socket){
socket.on('disconnect', function(){
// Do stuff here
});
});
So... nowadays I think this would be a better (although harder to implement because you need node, socket, etc., but is not that hard; should take like 30 min or so if you do it first time) approach than the unload version.
The selected answer is correct that you can't guarantee that the browser sends the xhr request, but depending on the browser, you can reliably send a request on tab or window close.
Normally, the browser closes before xhr.send() actually executes. Chrome and edge look like they wait for the javascript event loop to empty before closing the window. They also fire the xhr request in a different thread than the javascript event loop. This means that if you can keep the event loop full for long enough, the xhr will successfully fire. For example, I tested sending an xhr request, then counting to 100,000,000. This worked very consistently in both chrome and edge for me. If you're using angularjs, wrapping your call to $http in $apply accomplishes the same thing.
IE seems to be a little different. I don't think IE waits for the event loop to empty, or even for the current stack frame to empty. While it will occasionally correctly send a request, what seems to happen far more often (80%-90% of the time) is that IE will close the window or tab before the xhr request has completely executed, which result in only a partial message being sent. Basically the server receives a post request, but there's no body.
For posterity, here's the code I used attached as the window.onbeforeunload listener function:
var xhr = new XMLHttpRequest();
xhr.open("POST", <your url here>);
xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
var payload = {id: "123456789"};
xhr.send(JSON.stringify(payload));
for(var i = 0; i < 100000000; i++) {}
I tested in:
Chrome 61.0.3163.100
IE 11.608.15063.0CO
Edge 40.15063.0.0
Try this one. I solved this problem in javascript, sending ajax call to server on browse or tab closing. I had a problem with refreshing page because on onbeforeunload function including refreshing of the page. performance.navigation.type == 1 should isolate refresh from closing (on mozzila browser).
$(window).bind('mouseover', (function () { // detecting DOM elements
window.onbeforeunload = null;
}));
$(window).bind('mouseout', (function () { //Detecting event out of DOM
window.onbeforeunload = ConfirmLeave;
}));
function ConfirmLeave() {
if (performance.navigation.type == 1) { //detecting refresh page(doesnt work on every browser)
}
else {
logOutUser();
}
}
$(document).bind('keydown', function (e) { //detecting alt+F4 closing
if (e.altKey && e.keyCode == 115) {
logOutUser();
}
});
function logOutUser() {
$.ajax({
type: "POST",
url: GWA("LogIn/ForcedClosing"), //example controller/method
async: false
});
}
Im agree with Felix idea and I have solved my problem with that solution and now I wanna to clear the Server Side solution:
1.send a request from client side to server
2.save time of the last request recived in a variable
3.check the server time and compare it by the variable of last recived
request
4.if the result is more than the time you expect,start running the
code you want to run when windows closed...
Use:
<body onUnload="javascript:">
It should capture everything except shutting down the browser program.

How to force logout user when close tab

I am developing a enterprise application using spring and struts. But, i'm getting issue on Tab close. how to force user logging out when close the browser tab or open same page on another tab.
i have been try using onbeforeunload but i am getting issue when application running on mobile browser. and i also have seen following sample but No one has clear explanation.
How to kill session when user closed the browser without logout
How to Detect Browser Window /Tab Close Event?
is there any solution to achieve this problem using javascript or from server?
Thanks
Finally I found a solution that worked!
When the user logs in I use sessionStorage to store that a user has been logged in:
sessionStorage.setItem('logged', true)
SessionStorage will hold that property until the tab or the browser closes.
So inside the application, I check if the SessionStorage still holds that property. If not then I logout the user and I redirect him to the login.
if (!sessionStorage.getItem('logged')) {
localStorage.removeItem('token')
navigator.sendBeacon('api/logout')
window.location.replace("/login") //redirect to login
}
For those who wonder what is navigator.sendBeacon you can read here to learn more. Practically I use it because I want the api/logout to be accessed even if the tab closes.
You may start a ajax request by page onload, and get a tracking session id/serial from server.
Later force all of the requests operations to include the tracking session you just gave to the page using the ajax call above.
If user opens up a new tab, the ajax load starts again. and As far as you check the live session tracks in the server with associated user, you may redirect the user to somewhere else or etc.
I don't think you may rely on onbeforeunload, just the same thing you experience.
As others stated, http is stateless, and cookies and requests are only stuffs you can check the state of the user.
At the server, session cookies could be invalidated by session timeout(if we assume user going to brew some coffee, e.g. it closes the page/tab).
And as explained in above solution, if he/she opens a new tab, the new ajax call might block him/her to perform a new login or etc.
I suggest you may find another solution instead of this approach you are trying to get. It's much easier and more senseful to sync the page state with the last opened page. Let the user opens 5 same page, and just sync them all with each other(simplest solution: refresh others when one gets updated)
You may have a javascript function and invoke it on "onunload" of body and in that script invoke backend code to invalidate the user session.
localStorage can be use to keep idle time for the application with multiple tabs are opened.
// Check browser support
if (typeof(Storage) !== "undefined") {
// Store an item to localStorage
localStorage.setItem("timeIdle", "0");
console.log(localStorage.getItem("idleTime"));
// Retrieve the added item
} else {
//display this message if browser does not support localStorage
console.log("Sorry, your browser does not support Web Storage.");
}
function func(){
$(this).keypress(function(e) {
localStorage.setItem("timeIdle", "0");
});
$(this).click(function(e) {
localStorage.setItem("timeIdle", "0");
});
timerIncrement();
}
function timerIncrement() {
var timeIdle = localStorage.getItem("timeIdle");
timeIdle = parseInt(timeIdle) + 1;
if (timeIdle >= 1) {
logoutCall();
window.location = window.location.origin+"/riskoffice-ui/Login";
}
localStorage.setItem("timeIdle", timeIdle.toString());
}
setInterval(func,1800000); //Runs the "func" function every second

What is the best way to implement idle time out for web application (auto log off)

I want to implement an idle time-out for the web application that we are building. I had earlier achieved this using AsynchronousSessionAuditor from codeplex, which essentially looks for the formsauthentication and session cookie timeout by constant polling.
But it has a draw back of not respecting the client side events, it will look for only last postback to decide when to log off.
The jquery plug jquery-idle-timeout-plugin from erichynds solves this issue of client side events but suffers from another drawback that is not able to recognise user is active on some other tab.
Is there anyone already fixed the TABBED browsing issue with jquery-idle-timeout-plugin already? Or is there any better approach of application time out for web applications (by the way this web app is build using asp.net f/w)
If I understand your question right, it is not possible, since there are no events triggered in javascript for activity outside of the current window/tab.
Unless you have a addon to go along with your website for each browser, which could monitor all activity in the browser, but that is not really a practical approach.
Well, you'd have to code it by hand, which is not really hard. You can use the onfocus and onblur functions to do something like this:
$(function() {
window.isActive = true;
$(window).focus(function() { this.isActive = true; });
$(window).blur(function() { this.isActive = false; });
showIsActive();
});
function showIsActive()
{
console.log(window.isActive)
window.setTimeout("showIsActive()", 2000);
}
function doWork()
{
if (!window.isActive) { /* Check for idle time */}
}
If you make a little search you can find that varaieties of this question have already been asked and answered, you can probably find a solution you can implement with one of the plugins you mentioned.
Try:
Run setTimeout only when tab is active
or
How to tell if browser/tab is active
EDIT--> ADDED:
Or I'd try a different approach. You could create a cookie with some hash and save that hash in your DB with a timestamp that updates whenever the window is active (you could check every 5 seconds or something, it's not an intensive request)
Then, do another check before(but in the same request) to see how much time has passed since the last timestamp and log them out if necessary.
it won't log them out isntantly when time has passed, but it will when they try to access the site either by opening it again or by focusing on the tab/window.

how to clear devise session on browser close?

I am using 'devise' for password authentication in my ruby on rails app. Once I successfully login and I close my browser and open a new browser window I am still logged in. Lets say I am using Chrome, I close all instances of chrome and then open a new one. I am still logged in. The same is the case when I am doing IE and Firefox too.
I would assume on close of window and opening a new window should establish a new session between the server and the browser isn't it? If not how do I achieve that?
i used tried doing clicking on logout button on browser window's onbeforeunload event, but it does not works as it logout of application on any form submit or link click.
window.onbeforeunload = function() {
$('#logout_cls').click();
};
and tried sending AJAX request to the sessions controller destroy action for clearing the session.
jQuery(window).bind(
"close",
function(event) {
$.ajax({
type: 'DELETE',
dataType: 'json',
url: $('#logout_cls').attr('href')
});
});
but all this did not work.
Turns out that the problem was how my session_store was configured.
MyApp::Application.config.session_store :active_record_store,
{:key => '_my_app_session', :secret => '5xb5x1g92e965b95b16e49x79gxx9999', :expire_after => 2.hours}
I removed the options and the issue was solved.
I have another question though and that is removing options does it have any potential risks? I am not storing any important data in the session obviously but putting in data I wouldn't want exposed.
My app would be going live in a few days it would be very helpful to get an answer for this. I googled things for an hour or so but didn't have too much luck.
Thanks
That is actually a feature. Facebook and pretty much all sites with authentication do it through the use of cookies.
Devise's option Rememberable "manages generating and clearing a token for remembering the user from a saved cookie". If you don't want that, then remove the remember_token string and remember_created_at datetime from your user model and remove the Remember Me button from your login page.

Categories

Resources