Simple Cross-domain JavaScript IFrame Communication
October 26, 2009
For the past day, I've been working on developing a bookmarklet. Part of it involves injecting a sign-in form as an iframe into the page the user is on. Keeping the form snuggled up in a remote iframe is crucial for keeping the credentials the person types far away from the peeking eyes of potentially-malicious code on the page (see "Same-origin policy").
Well this is great. *sigh* Now how do you go about sending legitimate messages back and forth between the two documents?
The Solution
The answer lies with the blend of two techniques: (A) sending the data by setting the fragment identifier for the remote document and then polling for the change, and (B) implementing HTML5'swindow.postMessage() method.
xcom_send
var xcom_send = function(win, msg, origin){
if (document.all ? document.documentMode : window.postMessage){
win.postMessage(msg, origin ? origin : '*');
}else{
win.location.hash = '#xcom-' + msg;
}
};
| Argument | Notes |
|---|---|
| win | The target window to receive the message. |
| msg | The string to send to win. This will be picked up by the callback setup with xcom_listen. |
| origin | (optional) The domain(s) to allow communication to. Accepts a string, or array of strings. Domains should be in the form of http://the.domain.com (make sure to not include a trailing slash). Important: Don't depend on this for security---this argument will only be acknowledged by modern browers: IE8, Chrome, and FF3 beta 3 so far. |
xcom_listen
var xcom_listen = function(win, cb, origin){
if (document.all ? document.documentMode : win.postMessage){
var receiveMessage = function(e){
var safe = origin == null || origin == '*';
if (!safe){
if (origin instanceof Array){
for (var i = 0; i < origin.length; i++){
if (origin[i] === e.origin){
safe = true;
break;
}
}
}else{
safe = e.origin === origin;
}
}
if (safe && cb) cb(e.data);
};
if (win.addEventListener) win.addEventListener('message',receiveMessage,false);
else if(win.attachEvent) win.attachEvent('onmessage',receiveMessage);
else win.onmessage = receiveMessage;
}else{
var l = win.setInterval(function(){
if (win.location.hash.substring(0,6)=='#xcom-'){
//window.clearInterval(l);
var msg = win.location.hash.substring(6);
win.location.hash = '';
if (cb) cb(msg);
}
}, 250);
}
};
| Argument | Notes |
|---|---|
| win | The window to listen for messages from. |
| cb | The callback function to receive any messages from win. The callback will be invoked with the message string as the first and only argument. |
| origin | (optional) The domain(s) to allow communication from. Accepts a string, or array of strings. Domains should be in the form of http://the.domain.com (make sure to not include a trailing slash). Important: Don't depend on this for security---this argument will only be acknowledged by modern browers: IE8, Chrome, and FF3 beta 3 so far. |
Usage Example
To listen for messages sent from aniframe in the document, use xcom_listen with a basic callback function:
var frame = document.getElementById('myframe');
xcom_listen(
frame.contentWindow || frame,
function(msg){
alert('From the iframe: ' + msg);
}, ['http://mydomain.com', 'http://www.mydomain.com']);
From inside the iframe, use xcom_send and window.top to send a message to be captured by xcom_listen in the parent document (seen above).
xcom_send(window.top, 'I like to talk and be heard!');No more antisocial cross-domain web apps!
The Commentary
Why is the “win” parameter used in “xcom_listen = function(win, cb, origin)”? It’s never used. Your example:
xcom_listen(document.getElementById('myframe').contentWindow...…doesn’t work in IE6; dropping “.contentWindow” from the window parameter works.
Do’h. You’re absolutely right. I intended to switch “window” over to “win” to make it more flexible, but totally spaced doing it. Thanks! I fixed it up. Thanks for letting me know about the IE6 bug as well.
Hey, nice post, just a doubt.
I Have a situation where in domain A i have an iframe with a login, this iframe has a src of domain B (which I dont have any access at all). I would like to know if the login was succesful or not, to display a message in my page in domain A. Can i do this with your code (i’m a bit newbie to understand all your code.
Thks in advance!
Unfortunately what you’re trying to do is not possible with this. In order for the library to work, you’ve got to have access to both domains, to transmit from one and listen from the other.
Your best bet would probably be to try sniffing the iframe’s URL (window.location) periodically to see if the frame’s been redirected to some “successful login” page. Other than that, you’re pretty out of luck…
Hey Brian, thanks for answering so quickly.
I’ve already tryed to check window.location but i get a permission error, so it isn’t useful.
I mean, i’ve done smth like the following on the load event of the iframe: alert($(’myFrame’).location); alert($(’myFrame’).location.href) both didn’t work, one gave me permission error.
I really need to do this for a site i’m finishing is the final response – as i saw in forums – it can’t be done?
Thanks again!
Grr, that was what I was afraid of…
What site are you trying to login to? I hate to say it, but, unless they provide some JSONP or OAuth system, I’m thinking you’re pretty out of luck. Sorry man :/
This is the site i’m trying to check:
http://www.cint.com/cpx/public_panelist_login.asp
I’ll have to contact them dont know how lol.
Thanks dude!
Is this possible to fetch elements from Cross domain iframe