Event triggering from iframe - javascript

I'm integrating a third party photo upload service with my app. So I'm loading it in my page via iframe.
When the upload service is done with uploading my photo it can either trigger certain event to my parent page i.e :
parent.$('body').trigger('photoUpload.complete');
or it triggers a function in the parent page i.e :
window.parent.reloadParentPage();
In any case I get this warning in my chrome console :
Uncaught SecurityError: Blocked a frame with origin "https://photoupload.com" from accessing a frame with origin "https://website.com".
I realize this is a security issue as described here :
http://www.w3.org/TR/2008/WD-access-control-20080912/
So I wanted to enable the origin https://photoupload.com to access my site. I did this in my controller :
after_filter :set_access_control_headers
Then the method :
def set_access_control_headers
headers['Access-Control-Allow-Origin'] = "https://photoupload.com"
headers['Access-Control-Request-Method'] = '*'
end
Please not that https://photoupload.com is the photo upload service and https://website.com is my website. (Imaginary names for example sake), but they are both hosted on heroku.
How do I make this work?
Saw similar questions that people had success with this :
Triggering a jQuery event from iframe
Update
Maybe a better question would be, in which app should I set the headers? I was assuming in my app?
Update II
Is there a better way to do this? Send action/event/something from iframe to the parent page, so the parent page can react in some way

As long as you don't have to support IE6 or IE7, the preferred way to send cross-domain messages between an iframe and its parent is to use window.postMessage(...).
Since you have the ability to modify the upload service, you should have it invoke something like this:
window.parent.postMessage('photoUpload.complete', 'https://website.com');
(the second parameter can be set to '*' to allow the iframe to send messages regardless of the containing page's domain, but that's correspondingly less secure - may not be relevant in your case though as no actual data is being sent).
and your site would use something like
if (!window.addEventListener) {
// IE8 support (could also use an addEventListener shim)
window.attachEvent('onmessage', handleMessage);
} else {
window.addEventListener('message', handleMessage, false);
}
function handleMessage(event) {
// check where the message is coming from - may be overkill in your case
if (event.origin==='https://photoupload.org') {
// check event type - again probably not required
if (event.data==='photoUpload.complete') {
// do your thing
}
}
}
And if you want to send messages back from the outer page to the iframe, it's basically the same setup but you send with:
iframe.contentWindow.postMessage(...)
If IE7 or IE6 support is required, postMessage() is not supported but you could use something like http://easyxdm.net/wp/

I guess this line should work as well
window.parent.$(window.parent.document).trigger('photoUpload.complete');
Explanation: In your code parent.$('body').trigger('photoUpload.complete');
'body' is referring to iframe body and not the parent window body.

Related

Access a button inside the IFrame

I am looking for a way to access a button inside the iFrame and trigger a click event when that button is clicked inside the iFrame that is on another domain.
Trying to go deeper into an element within the iFrame has proven difficult. Has anyone had success taking it this far?
Use an ajax call to the iframe's src to get its content, and render it as part of your site (which you then can hook).
You can't access the contents from an iframe from a different domain directly because that would be a security violation.
If i understand your requirements correctly
You can add a $('#iframe').load(function(){} which will watch the loading of iframe into your DOM.
After loading iframe you can attach an event listener to button click
var iframe = $('#iframe').contents();
iframe.find("#button").click(function(){
//write code to close the popup
});
The above process can be summarized as follows
$('#iframe').load(function(){
var iframe = $('#iframe').contents();
iframe.find("#button").click(function(){
$('#popup').close() //May depend on your popup implementation
});
});
The Problem here is that the same-origin policy blocks scripts from accessing contents of site with other origin.
Actually origin consists of the following parts.
origin:<protocol(http/https)>://<hostname>:<port number>/path/to/page.html
The origin is considered to be different if protocol,host name and port number are not same.
In such cases you can not access the contents of one website from other website due to same-origin security policy.
In order to overcome it you have to use parent-child communication using window.postMessage().
FYI : https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage.
The Window.postMessage() method safely enables cross-origin communication.
Suppose that your parent website is example-parent.com and In Iframe your loading website example-iframe.com and let both are using http protocol. Below is how I solved the problem.
In parent website add event listener for messages to receive as follows.
window.addEventListener('message',receiveMessage,false);
function receiveMessage(event){
var origin = event.origin || event.originalEvent.origin;
if(origin.indexOf('http://example-iframe.com')>=0) // check if message received from intended sender
{
if(event.data=="intended message format") // check if data received is in correct format
{
// call functionality that closes popup containing iframe
}else{ // data received is malacious
return;
}
}else{ // message is not received from intended sender
return;
}
}
From Iframe post message to the parent website as follows.
Post message syntax : otherWindow.postMessage(message, targetOrigin, [transfer]);
function sendMessage(){
parent.postMessage('intended message format','http://example-parent.com');
}
Use postMessage() properly,otherwise it may lead to cross-site scripting attack.
I am Sorry to say this to you but, you can't.
Since that will be violating CORS (Cross-origin resource sharing) rules that browser has set and it won't let you break those. Since its the almighty.
It will give an error 'Access-Control-Allow-Origin' in your console .
Hope you find it helpful.
Still if want to do something in your website you can ask below, I might give you an alternate to do so.

JavaScript problems when launching site in iframe

I have set up an Articulate Storyline course (a Flash version accessed using the page "story.html" and an HTML5 version accessed using "story_html5.html"). It works fine when run directly, however, when I try to run everything in an iframe on the company server (linking to the course files on my personal server) I get JavaScript errors:
The course uses player.GetVar("HTML5spelaren") to access a variable called HTML5spelaren, which is located on the story_html5.html page itself. When running in an iframe I get a "Permission denied to access property 'HTML5spelaren'".
Finally the course uses the JavaScript var newWin=document.window.open("report.html", "Kursintyg"); to display a course completion certificate in a new window. When running in an iframe however this results in a "Permission denied to access property 'open'".
Is there a way to rewrite the JavaScripts to get around this? I need to be able to detect if the course is running in Flash or HTML5 mode (that's what I use the variable in story_html5.html for), as well as being able to use JavaScript to open a new page from within the iframe when clicking on a link.
Page structure:
https://dl.dropboxusercontent.com/u/11131031/pagestructure.png
/Andreas
There's a way for different domains to speak to one another via javascript. You can use postMessage: https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage
In your case, in story.html or story_html5.html could use something like:
parent.postMessage(HTML5spelaren, parent_domain);
and you add an event listener in the company page:
window.addEventListener("message", receiveMessage, false);
And in receiveMessage function you retrieve the data that you need. Something like:
function receiveMessage(event){
your_variable = event.data
}
Same logic can be probably be applied to your popup.
You can post from child to parent or from parent to child.
My guess is that content you're linking to in the iFrame is on a different server/domain. If so, the error is a security feature to stop cross-site scripting (XSS) attacks.
Consider putting both the parent iFrame and the articulate content (child) on the same server. This should eliminate the problem.

IFRAME resize across subdomain on .Net platform

It's not any of the possible duplicates present on stackoverflow. Here is the problem statement.
I've two application running on two different subdomains.
http://abc.myclient.net:1234/MyApplicationSite: This is a Sharepoint site which is the hosting environment. My user control is deployed on this environment on the master page. The user control has a div element with an iframe. The src of iframe is set to the ASP.Net Web application of different subdomain discussed later. I'm using jquery-UI library to open up the div containing iFrame as a dialog popup. Let's call this sharepoint site as consumer henceforth.
http://xyz.pqr.myclient.net:8080/MyApp/MyPage.aspx: This is an ASP.Net Web Application on .Net platform 3.5. It serves as content inside iframe explained earlier. Let's call this provider henceforth. It contains an accordion control from jQuery-UI. On click of accordion head activate event is fired normally and I execute a javascript function to resize parent iFrame. On same domain I achieved this by calling window.parent DOM elements and then resized iFrame height from this web application. But I get Permission denied error now when consumer and provider resides on two different subdomains. I understand that this is due to the Same Origin Policy.
To fix this I just wanted to transfer the height value of web application to the host Sharepoint page. An event from from web application is fired - accordionactivate which I need to listen on the Sharepoint page javascript using window.addEventListener or window.attachEvent whichever is applicable. Since, Iframe is part of Sharepoint page and we have the new height value, so we can resize it after listening to the event raised from content window. To implement this, I used the following approaches but haven't succeeded so far.
document.domain
window.postMessage()
easyXDM
polling
The problem I face is that the event is not listened at the consumer end (SP site) and hence iFrame resize is not triggered. Let me know if any body have faced similar issues. I need a neat and clean cross - browser solution for this. Multiple approaches are welcome.
My solution was to set up a reverse proxy server (using Apache) that pulled pages from multiple domains into (what appeared to the client) as a single domain and then I could easily reach across the IFrame border. The way this works, your end users get a single domain name for the proxy server. when you code the IFrame, use the proxy server address which will be routed back to MyApp. See this page for info on reverse proxy: Apache Geronimo
only in your case, add this to the httpd.conf file:
ProxyPass / http://abc.myclient.net:1234/MyApplicationSite
ProxyPass /MyApp http://xyz.pqr.myclient.net:8080/MyApp/MyPage.aspx
ProxyPassReverse /MyApp http://xyz.pqr.myclient.net:8080/MyApp/MyPage.aspx
All we need is correct usage and understanding of postMessage and jQuery. First on provider i.e. on ASP.Net Web Application, inside my accordion js code, I call postSize method.
$(".my-accrdion-class").accordion({
collapsible: true,
active: false,
heightStyle: "content",
activate: function (event, ui) {
postSize();
}
});
Then I define postSize like this.
function postSize() {
var height = jQuery(iFrameContentSelector).height();
data = { 'height': height };
window.parent.postMessage(JSON.stringify(data),"*");
}
You will need to include the JSON2 library. Here, if you have only one consumer then instead of * give proper URL with port number and protocol too. In our case I apply a check on consumer end for the origin and the data passed is non - sensitive. So, no security concerns at this point.
Now on consumer end, i.e. inside the javascript for user control. Add the following code.
// Here "addEventListener" is for standards-compliant web browsers and "attachEvent" is for IE Browsers.
var eventMethod = window.addEventListener ? "addEventListener" : "attachEvent";
var eventer = window[eventMethod];
var messageEvent = eventMethod == "attachEvent" ? "onmessage" : "message";
// Listen to message from child IFrame window
eventer(messageEvent, function (e) {
if (e.origin == 'http://xyz.pqr.myclient.net:8080') {
var window_height = JSON.parse(e.data);
var frame = $("#iFrameDiv iframe");
frame.height(window_height.height);
}
}, false);
That's it. Here iFrameDiv contains my iFrame which is a server control.
The same discussion can be found here
Cheers,
Naman

Is there a way to retrieve an ajax response sent from an iframe?

I was wondering if there is any way to retrieve the response of an ajax request sent from an iframe.
The iframe does something like the following:
script in iframe sends an ajax request
iframe gets a response, and updates content within iframe
What I would like to do is to intercept that request outside of the iframe, and use that information to update the page.
If you go into chrome's dev tools, and go to the network tab, you can see the request/response that the iframe makes. It would be nice if there is a window event or something similar that triggers everytime a response comes in on the page.
NOTE: the iframe is a page that is not in the same domain.
If the child iframe is in a different domain to the parent, you can't really do anything that's going to work across browsers. See jasonjifly's answer below for techniques that will work on some browsers assuming you have control over the client scripts on both frames.
If the parent and child are on the same domain, then you can achieve what you are looking for.
For example, assuming you're using jquery, you could have this code in your iframe:
$.ajax(function() {
.....
complete: function() {
window.parent.onAjaxComplete('Hi there');
}
});
Along with this code in your parent frame:
function onAjaxComplete(msg)
{
alert(msg);
}
To achieve similar results in a cross-domain scenario you could have the parent frame poll the server. When the server receives an ajax request from the child iframe it could then alert the parent page via the polling service. Obviously this would only be suitable if you have control over the services called by the child iframe.
In theory, it's forbidden to communicate between two cross domain iframe, due to the same-origin policy, please refer to this page: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Same_origin_policy_for_JavaScript
Before HTML5, we have some workarounds:
If you just want to use iframe to get cross domain data, you can use JQuery:JSONP, the essence is using . "" allows executing a cross domain javascript.
Another way is "An iframe in an iframe in an iframe", you can refer to this page: http://blog.cakemail.com/the-iframe-cross-domain-policy-problem/
HTML5:
window.postMessage, refer to this page: https://developer.mozilla.org/en-US/docs/Web/API/window.postMessage
HTML5 CORS, you need to configure server, refer to this page: http://www.html5rocks.com/en/tutorials/cors/
My recommendation is using solution 1, it can work on almost all mainstream browsers.
(function() {
var origOpen = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function() {
this.addEventListener('load', function() {
var json = $.parseJSON(this.responseText);
});
origOpen.apply(this, arguments);
};
})();

Cross domain iframe content load detection

I have a rather interesting problem. I have a parent page that will create a modal jquery dialog with an iframe contained within the dialog. The iframe will be populated with content from a 3rd party domain. My issue is that I need to create some dialog level javascript that can detect if the content of the iframe loaded successfully and if it hasn't within a 5 second time frame, then to close the dialog and return the user to the parent page.
I have researched numerous solutions and only two are of any true value.
Get the remote site to include a javascript line of document.domain = 'our-domain.com'.
Use a URL Fragment hack, but again I would need the request that the remote site
able to modify the URL by appending '#some_value' to the end of the URL and my dialog window would have to poll the URL until it either sees it or times out.
Are these honestly the only options I have to work with? Is there not a simpler way to just detect this?
I have been researching if there's a way to poll for http response errors, but this still remains confined to the same restrictions.
Any help would be immensely appreciated.
Thanks
The easiest way (if you can get code added to the external sites) is to have them add an invisible iframe pointing to a special html file on your domain. This could then use parent.parent.foo() to notify the original window about the load event.
Listening for the "load" event will only tell you if the window loaded, not what was loaded or if the document is ready for interaction.
Nicholas Zakas has an article about detecting if an iframe loaded: http://www.nczonline.net/blog/2009/09/15/iframes-onload-and-documentdomain/. Basically you have this code snippet:
var iframe = document.createElement("iframe");
iframe.src = "simpleinner.htm";
if (iframe.attachEvent){
iframe.attachEvent("onload", function(){
alert("Local iframe is now loaded.");
});
} else {
iframe.onload = function(){
alert("Local iframe is now loaded.");
};
}
document.body.appendChild(iframe);
I haven't tested it, but I'm pretty sure jQuery should be able to handle it by doing something like $("#iframe").load(function () { alert("Local iframe is now loaded."); });
You could try using postMessage for communication between frames.
This will require the remote site to include some specific JavaScript to post a message to the parent document when it has finished loading.
It's possible to do this with an onload handler on the iframe itself. Unfortunately (surprise!) IE makes it difficult. The only way I could get this to work was to compose HTML for the iframe, then append it to the document with innerHTML. Then I have to poll to see when the iframe appears in the DOM, which varies depending on if the page is loading. Here's a link to the source: http://svn.openlaszlo.org/openlaszlo/trunk/lps/includes/source/iframemanager.js
See create(), __finishCreate() and gotload(). Feel free to take a copy of this and use it yourself!
Regards,
Max Carlson
OpenLaszlo.org
This is how I detected the loading of a Cross-Domain Iframe,
Set a unique id for the iframe ( U may use any sort of identifier, it doesn't matter )
<iframe id="crossDomainIframe" src=""> </iframe>
Set window event listener:
document.getElementById("crossDomainIframe").addEventListener('load',
function actionToPerform(){
//Do your onLoad actions here
}
)
In any case you will need some sort of cooperation from the other domain's server, as you are trying to abuse the Same Origin Policy (SOP)
The first solution document.domain=... won't work if domains are different. It works only for subdomains and ports, as described in the link above.
The only option that allows cross domain communication without polling is JSONP or script injection with a JS function callback. This method is available in all Google APIs and works well.
We've explained on our blog a way to sandbox those calls in an iframe to secure them. While postMessage is better now, the window.name hack has the advantage of working on old browsers.
Ironically, SOP does not prevent you to POST anything to another domain. But you won't be able to read the response.

Categories