Summary:
Basically, I'm using a background page to listen to events, such as: onStartup, onInstalled and cookies.onChanged to decide which page should be displayed to the user when the browserAction is clicked. My question regards the latter and how it is triggered.
Code sample:
chrome.cookies.onChanged.addListener(function(info){
if(info.cookie.name === "dummycookie"){
/* Possibilities of info.cause (as described in the docs):
* evicted
* expired
* explicit (it's used when setting or removing a cookie)
* expired_overwrite
* overwrite
*/
if(info.cause == "overwrite" || (info.cause == "explicit" && !info.removed)){
// Cookie was set (explicit or overwrite)
chrome.browserAction.setPopup({ popup: "dummy1.html" });
}
else{
// Cookie was removed (evicted, expired or expired_overwrite)
chrome.browserAction.setPopup({ popup: "dummy2.html" });
}
}
});
The thing is, although the code above handles explicit calls just fine (cookies.set & cookies.get), it doesn't seem to trigger when a cookie life-span expires..
From the debugging sessions I conducted, the code is only triggered when a explicit call is made after the cookie's expected expiration date.
E.g. if I make a call like cookies.getAll() after the supposed expiration time, the browser realizes that the cookie has expired and only then the event is triggered.
Did I miss anything ? Can anyone please enlighten me if I'm misusing the cookies API or if I misunderstood the mechanic behind it ?
Any help is greatly appreciated !
Best regards,
For rare actions such as opening the browser action popup, you'd better actively query the cookies API for the latest state of the relevant cookie, instead of listening for cookie changes via the chrome.cookies.onChanged, because:
If your observed bug is real, then this will work around it.
Since the popup is not opened very often, it's quite a waste of resources to keep a background/event page alive merely to get notified of cookie changes, especially given the alternative method presented in this answer.
Example (popup.js, requires activeTab and cookies permission):
// Example: Get the value of the _gaq cookie for the top-level frame of the
// current tab.
chrome.tabs.query({
active: true,
currentWindow: true
}, function(tabs) {
// Need activeTab permission to read url, e.g. http://example.com/home
var url = tabs[0].url;
chrome.cookies.get({
url: url,
name: '_gaq'
}, function(cookie) {
// TODO: Do something with cookie, e.g. by updating the view (via DOM).
});
});
Related
the code below checks whether a url is loaded and then logs to the console. I would like to know if there is simple, clean method to check if a page is loaded from bfcache or http cache? Firefox documentation states that the load event should not be triggered if I go from URL A to B and then hit the back button to URL A, but this is not my experience, both load and PageShow is logged regardless, does anyone know why?
var tabs = require("sdk/tabs");
function onOpen(tab) {
tab.on("pageshow", logPageShow);
tab.on("load", logLoading);
}
function logPageShow(tab) {
console.log(tab.url + " -- loaded (maybe from bfcache?) ");
}
function logLoading(tab) {
console.log(tab.url + " -- loaded (not from bfcache) ");
}
tabs.on('open', onOpen);
I am not sure whether there is any purposeful API for that but a workaround that came to mind is to check value of the performance.timing.responseEnd - performance.timing.requestStart. If it is <= 5 then most likely it is HTTP or back-forward cache. Otherwise, it is a download from the web.
A way to recognize a return to the page through a back button instead of opening up a clean URL is to use history API. For example:
// on page load
var hasCameBack = window.history && window.history.state && window.history.state.customFlag;
if (!hasComeBack) {
// most likely, user has come by following a hyperlink or entering
// a URL into browser's address bar.
// we flag the page's state so that a back/forward navigation
// would reveal that on a comeback-kind of visist.
if (window.history) {
window.history.replaceState({ customFlag: true }, null, null);
}
}
else {
// handle the comeback visit situation
}
See also Manipulating the browser history article at MDN.
I have a web app that I would like to restrict to a single browser tab or window. So the idea is a user logs in and if they open a link in a tab/window or open a new browser tab/window it kills their session. I know many are against this but that's how the app needs to be.
The controller checks if the user is logged in via:
if (!isset($_SESSION['user_logged_in'])) {
Session::destroy();
header('location: '.URL.'login');
}
I have tried setting $_SESSION['user_logged_in'] to false if its true but then obviously you don't go any further than one page.
Is there a way to destroy the session when a new browser tab or window is opened? I'm guessing probably jquery/javascript but not across that side of things.
It's very complex to achieve, unfortunately.
And almost impossible to do it true cross-browser and supported by every browser.
Technically, every new browser tab doesn't differ from the latter, form server's point of view. They share cookies and session too.
The only things that differ is JavaScript session. Say, an example: a site that is fully AJAX-based. First page is always login page. Then everything's changed with AJAX. If you dare to open another tab with this site it will open the first page which is always logging you out be default, for example. This can make it possible, but it's very complex.
New technologies stack like localStorage might make this possible, where you can communicate between tabs sending messages in localStorage. But this isn't fully cross-browser and isn't supported by all browsers versions.
So if you are ok with only limited choice of latest browsers — then dig on localStorage and postMessage.
Just to piggy back on what Oleg said, it would be incredibly difficult since HTTP is stateless and browser tabs share data. One potential way of doing it COULD be on the front end, but a very specific set of circumstances would need to be present and they could easily be bypassed. IF the application is a SPA and the primary body is only loaded once, you could potentially generate a key on the body load and send that with each request. Then, if the body is reloaded (say in a new tab or new window), you could generate a new key which would start a new session.
However, the real question is why you would want to do this. Your user experience will suffer and no real security gains exist.
I have some solution and I want share it with you.
To restrict user to only one tab per session, you may use cookie. I describe here how you may build your webapp in order to archieve that goal.
Each time the web module needs to render the auth/login page, create and store a cookie with a given name. Let's call it browserName. The value of the cookie must be a generated value. You may use java.util.UUID if your programming language is java.
When the browser finished loading your auth/login page, set the browser's name with the generated cookie value. You have to know how to read cookie using JavaScript.
Each time the user load other page than auth/login page, check whether the current browser's name is that one stored in the cookie. If they are differents, prompt user and then you can run a snipt that reset session and redirect to auth/login page.
The following is an example of implementing what I've said.
Snipt to be added in the method that runs before your login page in shown Map<String, Object> v$params = new TreeMap<>();
v$params.put("path", "/");
FacesContext.getCurrentInstance()
.getExternalContext()
.addResponseCookie("browserName", UUID.randomUUID().toString(), v$params);
The mini JavaScript library that help you with cookie and other. Add it globally in your webapp.
/**
* http://stackoverflow.com/questions/5639346/shortest-function-for-reading-a-cookie-in-javascript
*/
(function() {
function readCookie(name, c, C, i) {
if (cookies) {
return cookies[name];
}
c = document.cookie.split('; ');
cookies = {};
for (i = c.length - 1; i >= 0; i--) {
C = c[i].split('=');
cookies[C[0]] = C[1];
}
return cookies[name];
}
window.readCookie = readCookie; // or expose it however you want
})();
// function read_cookie(k,r){return(r=RegExp('(^|;
// )'+encodeURIComponent(k)+'=([^;]*)').exec(document.cookie))?r[2]:null;}
function read_cookie(k) {
return (document.cookie.match('(^|; )' + k + '=([^;]*)') || 0)[2];
}
/**
* To be called in login page only
*/
function setupWebPage(){
window.name = read_cookie("browserName");
}
/**
* To be called in another pages
*/
function checkWebPageSettings(){
var curWinName = window.name;
var setWinName = read_cookie("browserName");
if( curWinName != setWinName){
/**
* You may redirect the user to a proper page telling him that
* your application doesn't support multi tab/window. From this page,
* the user may decide to go back to the previous page ou loggout in
* other to have a new session in the current browser's tab or window
*/
alert('Please go back to your previous page !');
}
}
Add this to your login page <script type="text/javascript">
setupWebPage();
</script>
Add this to your other page template <script type="text/javascript">
checkWebPageSettings();
</script>
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.
I am making an extension that can be ON or OFF, which I can check via a flag stored in local data.
When ON, I want to listen for all webRequests and redirect them like so:
chrome.webRequest.onBeforeRequest.addListener(
// callback
function(info) {
console.log("Got request: " + info.url + "\n Going to redirect");
return {redirectUrl: chrome.extension.getURL("redirect.html")};
},
// filters
{
urls: [
"<all_urls>"
]
},
// extraInfoSpec
["blocking"]);
But when OFF I do not want the event to fire at all. Ideally when OFF I wouldn't even be listening to events (if that would cut down on performance overhead in any significant way). One option I see is checking the flag in my callback function and simply not redirecting if OFF, but that still has the event being handled. As I understand it, the event will not be handled if the RequestFilter does not pass. Can I modify the RequestFilter to also check my ON/OFF boolean flag before trying to handle the event? Or is the RequestFilter only meant for checking URLs, headers, etc.?
Also my main reasoning for wanting to only handle events when ON is that it seems like a needless performance hit to try to handle EVERY webRequest - even if briefly. Would an immediate flag checking in the callback function not make any noticeable impact on performance anyway?
I am new to Chrome Extension dev and webdev in general, so if there is a much cleaner/easier way of doing this then please let me know.
Thanks!
The chrome.webRequest API cannot be used on event pages.
If you implement ExpertSystem's answer, then your extension won't add overhead to requests, but it will still waste memory (because using the webRequest API implies that you're using background pages. These pages always remain active even when the extension appears to do nothing).
The chrome.declarativeWebRequest is similar to the webRequest API, except that its API is declarative, allowing it to be used on event pages as well. The only downside of the API is that it is currently only enabled on the beta or dev channel. It will eventually be available on the stable channel though, probably within a few releases.
The following example shows how to redirect any URL whose host contains "google" ("google.com", "www.google.nl", but NOT "notgoogle.com") to a page within your extension:
var rules = [{
id: 'redirect-to-my-extension',
conditions: [
new chrome.declarativeWebRequest.RequestMatcher({
url: {
hostContains: '.google.'
}
})
],
actions: [
new chrome.declarativeWebRequest.RedirectRequest({
redirectUrl: chrome.runtime.getURL('redirect.html')
})
]
}];
// Whenever you're ready...
chrome.declarativeWebRequest.onRequest.addRules(rules);
// To disable the rules, simply remove the rules (by the previously specified id)
var ruleIds = rules.map(function(rule) { return rule.id; });
chrome.declarativeWebRequest.onRequest.removeRules(ruleIds);
This is merely an example. The declarativeWebRequest API has lots of other ways to construct conditions or actions, just take a look at the reference documentation and URL filters.
RequestFilters do not allow you to specify arbitrary conditions (such as if a flag is set). You can un-register the listener using removeListener() whenever the flag is set to OFF and register it back when the flag is set to ON. E.g.:
function myListener(...) {...}
function setEnabled(enabled) {
localStorage.enabled = enabled ? 'ON' : 'OFF';
if (enabled) {
chrome.webRequest.onBeforeRequest.addListener(myListener);
} else {
chrome.webRequest.onBeforeRequest.removeListener(myListener);
}
}
function isEnabled() {
return (localStorage.enabled !== 'OFF'); // <-- 'ON' by default
}
setEnabled(isEnabled());
i have a problem.
I am working on a chatting application. I want to kill the session if user closes the browser window without logging off. I used 'beforeunload' function but it also fires when a postback event is fired so it's not good for me.
Please help if anyone have any idea about it.
If you use polling to get the chat data, you should kill the session if you don't get a polling request from the client for a given time.
Client:
setInterval (pollData, 10000); /* poll for new data each 10 seconds */
Server:
if (clientX.LastPollTime is over 30 seconds ago) {
clientX.killSession();
}
I suggest you to use the Alexanders approach, but In most cases setting interval time wont alone solve this problem. Because the user may be idle for some time and it may exceed the timeout period.
In order to avoid this, yo need to add one more condition over this.
if the user is idle for the timeout period then Just make an AJAX request to server and update the client status as idle.
this will avoid logging off the session if the user is idel for certain time.
And you can terminate the session if the server didnt recieve any response from client in a specified time and the status is not updated to idle (during browser close or any application hangups).
yup dear, it is okey, but in second thing as you specified that that server didn't receive any response, in my code server only checks the application session and it will find it so it will work. what i want that if the user not log off then the page is killed and after that how can we call any ajax or xmlhttp request from client side to set the application session to offline.
so please guys tell me something this is the only thing is not going well. and thanx for your response.
As you said the event window.onbeforeunload fires when the users clicks on a link or refreshes the page, so it would not a good even to end a session.
However, you can place a JavaScript global variable on your pages to identify actions that should not trigger a logoff (by using an AJAX call from onbeforeonload, for example).
The script below relies on JQuery
/*
* autoLogoff.js
*
* Every valid navigation (form submit, click on links) should
* set this variable to true.
*
* If it is left to false the page will try to invalidate the
* session via an AJAX call
*/
var validNavigation = false;
/*
* Invokes the servlet /endSession to invalidate the session.
* No HTML output is returned
*/
function endSession() {
$.get("<whatever url will end your session>");
}
function wireUpEvents() {
/*
* For a list of events that triggers onbeforeunload on IE
* check http://msdn.microsoft.com/en-us/library/ms536907(VS.85).aspx
*/
window.onbeforeunload = function() {
if (!validNavigation) {
endSession();
}
}
// Attach the event click for all links in the page
$("a").bind("click", function() {
validNavigation = true;
});
// Attach the event submit for all forms in the page
$("form").bind("submit", function() {
validNavigation = true;
});
}
// Wire up the events as soon as the DOM tree is ready
$(document).ready(function() {
wireUpEvents();
});
This script may be included in all pages
<script type="text/javascript" src="js/autoLogoff.js"></script>
Let's go through this code:
var validNavigation = false;
window.onbeforeunload = function() {
if (!validNavigation) {
endSession();
}
}
// Attach the event click for all links in the page
$("a").bind("click", function() {
validNavigation = true;
});
// Attach the event submit for all forms in the page
$("form").bind("submit", function() {
validNavigation = true;
});
A global variable is defined at page level. If this variable is not set to true then the event windows.onbeforeonload will terminate the session.
An event handler is attached to every link and form in the page to set this variable to true, thus preventing the session from being terminated if the user is just submitting a form or clicking on a link.
function endSession() {
$.get("<whatever url will end your session>");
}
The session is terminated if the user closed the browser/tab or navigated away. In this case the global variable was not set to true and the script will do an AJAX call to whichever URL you want to end the session
This solution is server-side technology agnostic. It was not exaustively tested but it seems to work fine in my tests
PS: I already posted this answer in this question. I am not sure I should answer multiple questions that are similar or post a reference?
If you have control of sessionID cookie, just set its lifetime to 0, that makes the session die on browser close. The lifetime of the session on the open window can be controled from the server side storing the time last seen in the session and checking
if(isset($_COOKIE[session_name()])) {
setcookie(session_name(), $_COOKIE[session_name()], 0, "/"); // die # browser close
}
if(isset($_SESSION['last_time'])){
if( ( time() - $_SESSION['last_time'] ) > 300 ){ // 5 minutes timeout
// here kill session;
}
}
$_SESSION['last_time'] = time();
In the client side you can use the Daniel Melo's answer. I'm using it with one small change:
function endSession() {
// $.get("<whatever url will end your session>");
// kill the session id
document.cookie = 'MYOWNSESSID=; path=/';
}
The only pending matter is that i can't wireup events to input type[buttons] yet, i have made it with raw code, but the all thing works.