I'm trying to create my own XMLHttpRequest framework to learn how this things work internally.
A thing that puzzles me is that I cannot find how to catch a "Same origin" exception.
The idea behind this is that I try to load a URL, if I get a Same origin exception, I re-request the URL through a proxy script local for the script. The reason I do this is because I need to access production data from a development sandbox and I want it to be as transparent as possible for the script itself.
I know it's a bad practice but this is the least intrusive way of doing this at the moment :)
Just to clear things - I don't want to bypass same origin, I just want to catch the thrown exception so I can do something about it.
Here is the code I currently use for my xhr:
var net = function (url, cb, setts){
this.url = url;
this.cb = cb;
var oThis = this;
if (!this.xhr) {
this.xhr = new XMLHttpRequest();
this.xhr.onreadystatechange = function() {
if (oThis.xhr.readyState == 4 && oThis.xhr.status == 200) {
document.body.innerHTML += "RS: "+oThis.xhr.readyState+"; ST:"+oThis.xhr.status+"; RP:"+oThis.xhr.responseText+"<br>";
}
else {
// do some other stuff :)
document.body.innerHTML += "RS: "+oThis.xhr.readyState+"; ST:"+oThis.xhr.status+"; RP:"+oThis.xhr.responseText+"<br>";
}
}
}
this.xhr.open("GET", url,true);
this.xhr.send();
} // It's WIP so don't be scared about the unused vars or hardcoded values :)
I've tried to try...catch around xhr.send(); but no avail, still can't catch the exceptions.
Any ideas or pointers would be greatly appreciated.
xhr.onreadystatechange = function() {
if (xhr.readyState==4) {
if (xhr.status==0) {
alert("denied");
} else {
alert("allowed");
}
}
}
Are you sure it's actually supposed to throw an exception? I can't see anything in the specifications: http://www.w3.org/TR/XMLHttpRequest/#exceptions Looks like it does. My bad.
In either case, you can always check the domain of the incoming string against the domain of the page the user is currently on.
FWIW, as you can see by this jsFiddle (open up Web Inspector), Chrome doesn't really throw an exception. It just says "Failed to load resource".
Related
I've got some code running in an iframe on 3rd-party sites. Some will be directly in the top page, some will be inside another iframe and some of these may be cross-domain. I need to find a way to get the URL value of the top page using any means necessary.
The furthest I can go up due to cross-domain policy is until the browser stops what the code is doing. I catch the error and look at the referrer of the current window context I'm in. Most cases the page above this is the top page, but not necessarily.
The only way I can see around this is building up a list of URLs which I think are the top page, and then sending a bot with a JS browser validate by seeing if the iframe my code got up to was in fact directly nested in them.
That's still not particularly accurate though, and I'm sure there must be another way of doing it...
Thanks to anyone who can help.
There is actually a way to get the domain in both Chrome and Opera, (in multiple nested cross-domain iframes), though it is not possible in other browsers.
You need to use the 'window.location.ancestorOrigins' property.
I have created a snippet of code below, which should work for you and if you think you can improve the code or comments, please don't hesitate to edit the gist on Github so we can make it even better:
Gist: https://gist.github.com/ocundale/281f98a36a05c183ff3f.js
Code (ES2015):
// return topmost browser window of current window & boolean to say if cross-domain exception occurred
const getClosestTop = () => {
let oFrame = window,
bException = false;
try {
while (oFrame.parent.document !== oFrame.document) {
if (oFrame.parent.document) {
oFrame = oFrame.parent;
} else {
//chrome/ff set exception here
bException = true;
break;
}
}
} catch(e){
// Safari needs try/catch so sets exception here
bException = true;
}
return {
'topFrame': oFrame,
'err': bException
};
};
// get best page URL using info from getClosestTop
const getBestPageUrl = ({err:crossDomainError, topFrame}) => {
let sBestPageUrl = '';
if (!crossDomainError) {
// easy case- we can get top frame location
sBestPageUrl = topFrame.location.href;
} else {
try {
try {
// If friendly iframe
sBestPageUrl = window.top.location.href;
} catch (e) {
//If chrome use ancestor origin array
let aOrigins = window.location.ancestorOrigins;
//Get last origin which is top-domain (chrome only):
sBestPageUrl = aOrigins[aOrigins.length - 1];
}
} catch (e) {
sBestPageUrl = topFrame.document.referrer;
}
}
return sBestPageUrl;
};
// To get page URL, simply run following within an iframe on the page:
const TOPFRAMEOBJ = getClosestTop();
const PAGE_URL = getBestPageUrl(TOPFRAMEOBJ);
If anybody would like the code in standard ES5, let me know, or simply run it through a converter online.
Definitely not possible without communicating with some sort of external system. The cleanest/most accurate way to gather data is to get the top window URL if the browser lets you, but catch errors and use the referer with a flag to note it's the referer.
This is a noob question.
What if I want to add logging to the java script application, which is running in a browser (IE, FF, etc.) ? As I understand I can not save log files in the client host. So, I have only two options: display my logging information in a new browser window (like "blackbird") or send the logging to the server.
Is it correct? What kind of logging do they usually use?
You can't "store" log files on client host. You can open a window and visualize it, but you (assuming you are running the Web Application) will never see it.
If you absolutely must get client side logs, you need to send them back to the server using AJAX. Here's a blog post I really liked about it.
Another possibility is the jsnlog library http://js.jsnlog.com/
It will let you send client side logs to the server.
Take a look at https://log4sure.com (disclosure: I created it) - but it is really useful, check it out and decide for yourself. It allows you to log errors/event and also lets you create your custom log table. It stores everything on its own server so you don't have to. It also allows you to monitor your logs real-time. And the best part, its free.
You can also use bower to install it, use bower install log4sure
The set up code is really easy too:
// setup
var _logServer;
(function() {
var ls = document.createElement('script');
ls.type = 'text/javascript';
ls.async = true;
ls.src = 'https://log4sure.com/ScriptsExt/log4sure.min.js';
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(ls, s);
ls.onload = function() {
// use your token here.
_logServer = new LogServer("use-your-token-here");
};
})();
// example for logging text
_logServer.logText("your log message goes here.")
//example for logging error
divide = function(numerator, divisor) {
try {
if (parseFloat(value) && parseFloat(divisor)) {
throw new TypeError("Invalid input", "myfile.js", 12, {
value: value,
divisor: divisor
});
} else {
if (divisor == 0) {
throw new RangeError("Divide by 0", "myfile.js", 15, {
value: value,
divisor: divisor
});
}
}
} catch (e) {
_logServer.logError(e.name, e.message, e.stack);
}
}
// another use of logError in window.onerror
// must be careful with window.onerror as you might be overwriting some one else's window.onerror functionality
// also someone else can overwrite window.onerror.
window.onerror = function(msg, url, line, column, err) {
// may want to check if url belongs to your javascript file
var data = {
url: url,
line: line,
column: column,
}
_logServer.logError(err.name, err.message, err.stack, data);
};
// example for custom logs
var foo = "some variable value";
var bar = "another variable value";
var flag = "false";
var temp = "yet another variable value";
_logServer.log(foo, bar, flag, temp);
i've been working on some kind of remote control for some time now and figured out a problem that confuses me:
The remote controle is supposed to control some kind of machine. The complete hardware based code is written in c by myself, but thats not really interesting for my problem.
Im using a webserver and a webpage to get access to some basic commands, just like driving forward, backward or turning motor on/off (i use a wlan accesspoint on the board in the machine to connect to it). So far everything works fine, but............:
My Problem is, that there are several options to control the functions of the machine (Terminal, wired-connected Notebook, remote control, "normal" working loop) at the same time. This means, if the machine switches on the motor by itself or a worker uses the terminal, it is neccessary for me to have a feedback on my remote control aswell (at the moment i switch the color of the different button-border from green=inactive to red=active).
The way i did that is easy explained:
If for an example the motor is switched on in c, i write a file with a decimal number that represents a binary code. Just like motor on is 0001 means i write a dec 1. Motor off would be 0010, means i write a dec 2. Because it is possible that there are more than only one function running at the same time i just pass values like 13 (motor on: 0001 = 1; forward: 0100 = 4; lights on: 1000 = 8; => 8+4+1=13). I use a binary AND comparsion to get the actual information about the status afterwards.
Nevertheless i cant refresh the page every two seconds to check the file i write to for changes, im using ajax to have a simple and fancy request possibility:
var http_request = false;
function RequestCommandStatus(url) {
http_request = false;
if (window.XMLHttpRequest) {
http_request = new XMLHttpRequest();
} else if (window.ActiveXObject) {
try {
http_request = new ActiveXObject("Msxml2.XMLHTTP");
} catch (e) {
try {
http_request = new ActiveXObject("Microsoft.XMLHTTP");
} catch (e) {}
}
}
if (!http_request) {
alert('Ended due to an error!');
return false;
}
http_request.open('GET', url, true);
http_request.onreadystatechange = control4Commands;
http_request.send(null);
}
and after that i do the comparsion stuff:
function control4Commands() {
if (http_request.readyState == 4) {
var answer = http_request.responseText;
if ((answer & 0x1) == 0x1){
//do something
}
if ((answer & 0x2) == 0x2){
//do something
}
if ((answer & 0x4) == 0x4){
//do something
}
if ((answer & 0x8) == 0x8){
//do something
}
}
}
setInterval("RequestValveStatus('myfile.txt')", 1000);
This works pretty good in Firefox but for some reason i got a strange problem in IE8 (even if i enabled every known scripting language and allowed pretty much complete access and control):
For some reason the request seems to be executed once, but after that there is no more interaction between the page and the server. The value of the file changes like it is supposed to. If i reload the page manualy, and for an example, the motor is still running, it changes the color of the border to red. In my opinion this should make sure that the request atleast succeded once.
By using the script debugger of the developer tools, i got the error message, that the http_request.open() failed because of access denied. My first suggestion was that it might be about the path or the type of the file i read from, but i wasnt able to fix it in IE8 with .html, .php, .txt and "several" other types. Also changing the path/origin didnt solved the problem.
I'd really like to discuss this problem with you and feel free to post me your ideas about this way of getting the actual status of the functions.
Greeting,
Ohemgi
I'm sorry for my bad english. Feel free to ask if you cant follow my explanations :-)
try to change
setInterval("RequestValveStatus('myfile.txt')", 1000);
into
setInterval(function() { RequestValveStatus('myfile.txt'); }, 1000);
I'm using the following function in a Firefox extension to check if a file exists in another extension:
function chromeFileExists(fileLoc) // in extension package
{
var xmlhttp = new window.XMLHttpRequest();
try {
xmlhttp.open("GET", "chrome://"+fileLoc, false);
xmlhttp.send(null);
var xmlDoc = xmlhttp.responseXML.documentElement;
}
catch(ex) {
return false;
}
return true;
}
But the problem is, of course, that if the file does exist, it actually loads the file before it tells me. Some of the files I'm querying are over 1MB in size, so I'd rather not load them into memory.
How to check for the existence and return without loading the file itself? I've tried working with onreadystatechange, but can't seem to figure it out.
I think I figured it out, after learning a few things about the way this type of request is handled:
status for local files is always "0" (not 200, etc.).
if async is true, it won't throw an exception if the file is not found.
it seems to skip readyState 3 for some reason - if async is false, readyState goes straight to 4.
if the first part of the chrome URL (the extension name) doesn't exist, it throws an exception on open().
If async is false and the file doesn't exist, it throws an exception on onreadystatechange.
If async is false and the file does exist, aborting onreadystatechange stops it from actually reading the file.
So, it seems the way to go is async=false, abort after the readyState change occurs successfully (to 4) and return true (the file exists). If there is an exception on open or onreadystatechange, return false (doesn't exist).
Here's the code, which seems to abort while xmlhttp.responseXML is still null if the file exists, and throws an exception if it doesn't:
function chromeFileExists(fileLoc) // in extension package
{
var xmlhttp = new window.XMLHttpRequest();
try {
xmlhttp.open("GET", "chrome://"+fileLoc, false);
xmlhttp.onreadystatechange=function() {
xmlhttp.abort();
}
xmlhttp.send(null);
}
catch(ex) {
return false;
}
return true;
}
You can use the onreadystatechange method of an XMLHTTPRequest and a setTimeout. I haven't actually tried this but I imagine it would go something like:
var clearhttp = function() {
xmlhttp.abort();
fileDoesNotExist = false;
}
xmlhttp.onreadystatechange=function() {
if (xmlhttp.readyState == 3) {
setTimeout(clearhttp, 250);
} else if(xmlhttp.readyState == 4 && xmlhttp.status == 404){
fileDoesNotExist = true;
}
}
what about:
xmlhttp.open("HEAD", "chrome://"+fileLoc, false);
Probably easier for you to test than it is for me since I haven't messed with extensions in a while and you can't exactly request chrome:// from a normal page :)
This may seem like a no-brainer, but I can't find a way to do this that isn't considered a security issue (other than the obvious ways)...
So, I want to build an add-on for Firefox to use with my team. Basically it will be a status bar icon letting us know if the authentication cookie for our tools site has expired, so we can tell without losing any work currently in the browser.
At first I thought I could have the add-on check the cookie, but this seems to be a huge hassle for such a simple idea. Then it occurred to me...DUH...that I could just have the add on try to access the main page of our site. If it gets a "Access Denied" response, it can show the icon for "Not Logged In", but if it gets anything else, it can show "Signed In".
However, all attempts to do this with AJAX are proving to be almost as difficult as my cookie attempts.
Is there a simple way, with javascript preferably, but in XUL otherwise, to say
var url = "http://example.com";
var response = pingURL(url, "blah);
status = (response = "Welcome!") ? "Signed in" : "Not Signed In";
where "pingURL" would be the method of "going" to the url and getting the response?
function checkAccess(url, callback) {
var xhr = new XMLHttpRequest();
xhr.open("GET", url);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if (xhr.status == 200) {
callback(true);
} else {
callback(false);
}
}
};
}
This should work... Just call with "checkAccess('http://example.com', function(ready){});" as an example where ready is a boolean value.
Exactly why do you consider cookies a huge hassle? That would undoubtedly be faster and probably simpler to implement. Reading cookies from chrome is simple and well-documented. Ask for help if you can't figure out how to parse the cookie.