Cross-Domain iframe communication - javascript

I have an iframe being created on a page, and the page's domain is being explicitly set to 'xyz.com' but the iframe's domain is defaulting to 'dev.xyz.com', which is the actual domain i'm developing for.
The problem is, when I try to access that iframe via iframe.contentWindow.document, it fails due to the difference in domain.
I've tried setting the iframe's src to a file with document.domain = 'xyz.com' but that doesn't seem to be doing the trick...
Any ideas?

Page inside iframe:
<script>
document.domain = document.domain;
</script>
It looks silly, but it works. See "What does document.domain = document.domain do?".

After some research, I found this jQuery plugin that makes postMessage backwards-compatible with older browsers using various tricks.
Here is a quick example showing how to send the height of the iframe's body to the parent window:
On the host (parent) page:
// executes when a message is received from the iframe, to adjust
// the iframe's height
$.receiveMessage(
function( event ){
$( 'my_iframe' ).css({
height: event.data
});
});
// Please note this function could also verify event.origin and other security-related checks.
On the iframe page:
$(function(){
// Sends a message to the parent window to tell it the height of the
// iframe's body
var target = parent.postMessage ? parent : (parent.document.postMessage ? parent.document : undefined);
$.postMessage(
$('body').outerHeight( true ) + 'px',
'*',
target
);
});
I've tested this on Chrome 13+, Firefox 3.6+, IE7, 8 and 9 on XP and W7, safari on OSX and W7. ;)

As a addition to the reference to the Ben Alman
plug in I thought I would post this working example. It ]rRelies on an iframe which has a source page containing jquery authentication & data query script which then passes the result to {other domain} parent window using the message plugin.
NB message plugin will break if using JQ v9 as JQV9 does not use "browser" referenced in the plugin
1st step:
Add the plugin code to both sending and receiving docs:
http://benalman.com/projects/jquery-postmessage-plugin/
2nd step:
Add this to sending doc:
$.postMessage(
$(X).html(),
'http://DOMAIN [PORT]/FOLDER/SUBFOLDER/RECIEVINGDOCNAME.XXX'
) ;
Where X can be a local var containing preformatted json array or other stuff, and the http url here is the address of the receiving document.
3rd step:
Add this to receiving doc:
$.receiveMessage(
function(event){
alert("event.data: "+event.data);
$("#testresults").append('<h1>'+event.data+'<h1>');
},
'http://DOMAIN.COM OR SOMETHING'
);
Where the http url is the DOMAIN of the sending document.
Good in IE 8, 9, FF16, safari Windows (windows wait x V9 not tested yet), safari x mac thing.
The result is any item you want out of another domain page (that you have access to..).

Related

Allowing two sites to communicate to know the current URL of an iframe

I'm trying to figure out a solution to allow an website to know what URL the user is on through an iframe.
Website 1: http://website.website.com (Remote Website, can only add javascript & html to the webpage)
Website 2: https://example.com (Fully Editable, php, html, js.. etc)
Current Code: (Of Website 2 (Example.com)
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="en-US" prefix="og: http://ogp.me/ns# fb: http://ogp.me/ns/fb#">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title>Website.com</title>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<script src="https://code.jquery.com/jquery-1.11.3.min.js"></script>
</head>
<body class="body_blank">
<script type="text/javascript">
jq = jQuery.noConflict();
jq(document).ready(function() {
var currentFramePath = '';
var iframe = '<iframe src="{src}" id="#iFrameContainer" style="position:fixed; top:0px; bottom:0px; right:0px; width: 100%; border: none; margin:0; padding:0; overflow: hidden; z-index:999999; height: 100%;">';
var urlFrame = getUrlParameter('currentFrame');
if(urlFrame != null && urlFrame != ''){
console.log("Frame not found");
jq('#iFrameContainer').html(iframe.replace('{src}', urlFrame));
currentFramePath = urlFrame;
}
jq('#iFrameContainer').click(function(){
console.log("Clicked in frame");
currentFramePath = jq(this).attr('href');
console.log(currentFramePath);
});
setInterval(function(){
window.location = window.location.href.split('?')[0] + '?currentFrame=' + currentFramePath;
console.log("Update Query");
}, 5000);
});
function getUrlParameter(sParam) {
var sPageURL = decodeURIComponent(window.location.search.substring(1)),
sURLVariables = sPageURL.split('&'),
sParameterName,
i;
console.log("Get Query");
for (i = 0; i < sURLVariables.length; i++) {
sParameterName = sURLVariables[i].split('=');
if (sParameterName[0] === sParam) {
return sParameterName[1] === undefined ? true : sParameterName[1];
}
}
};
</script>
<div id="wrapper" class="wrapper_blank">
<iframe src="http://website.website.com" id="#iFrameContainer" style="position:fixed; top:0px; bottom:0px; right:0px; width: 100%; border: none; margin:0; padding:0; overflow: hidden; z-index:999999; height: 100%;">
</div>
</body>
</html>
Problem
If I refresh the page (iframe) on example.com it refreshes and forgets the page that the user is/was on...
As you can see I have attempted to get it working by detecting their page through an iFrame however this is impossible due to it being on a different domain.
Solution?
I'm looking for some sort of solution to do something like described below, bare in mind there could be a better solution.
I want the website website.website.com to get the current path / url of the page the user is on (which is being viewed through an iframe) and for it to send this path/url through to example.com then example.com would update the session / temporary cookie / temporary local storage / variable... etc which would then mean it would adjust the query string to point itself to the correct URL for when the user refreshes their page resulting in the refresh correctly remembering the page they were on.
Attempt
I tried to use the postMessage function by putting the follow code on their respective sites:
Website 1 Extra Code
<script type="text/javascript">
setInterval(function() {
parent.postMessage(window.location.pathname, "https://website.com");
},1000);
</script>
Website 2 Extra Code:
var eventMethod = window.addEventListener ? "addEventListener" : "attachEvent";
var eventer = window[eventMethod];
var messageEvent = eventMethod == "attachEvent" ? "onmessage" : "message";
eventer(messageEvent, function(e) {
console.log('Parent Message: ', e.data);
}, false);
However nothing happens, no console messages or errors... just nothing.
I've even tried copying the likes of https://blog.teamtreehouse.com/cross-domain-messaging-with-postmessage but nothing in that helped :(
Any ideas what I am doing wrong and a way to resolve it to achieve this?
Thanks
Edits
I've tried the following js inside http://website.website.com but it didn't work:
localStorage.setItem('CurrentURLChecker', window.location.href)
if (localStorage.getItem('CurrentURLChecker')) {
if (window.parent.location.href == "https://website.com/" ) {
console.log("URL FOUND");
}
}
Uncaught DOMException: Blocked a frame with origin "http://website.website.com" from accessing a cross-origin frame at http://website.website.com/:251:44
EDIT - An example
Website 1 = "http://stackoverflow.serviceprovider.com"
Website 2 = "https://stackoverflow.com"
Website 2 contains an iframe which shows the exactly what Website 1 shows.
I am never going to visit Website 1, all clicks are done on Website 2
If I was to click on a link inside the iframe and it was to navigate to: http://stackoverflow.serviceprovider.com/this-new-page/ then Website 1 should be able to detect this and store the iframes location and remember it.
Now if I refresh my browser instead of the iframe loading http://stackoverflow.serviceprovider.com it would instead load the page they actually refreshed which is http://stackoverflow.serviceprovider.com/this-new-page/
The tab/window URL will always stay on https://stackoverflow.com/ but it would be a necessity to append a query string so the links can be made sharable.
It's that simple.
For security reasons, you can only get the url for as long as the contents of the iframe, and the referencing javascript, are served from the same domain.
If the two domains are mismatched, you'll run into cross site reference scripting security restrictions.
Since you can add javascript to the website 1 (http://website.website.com) you could create a session with javascript and save the current page the user visits in the cookies (as described here). When the user visits the home page of website on (which is happening, when the user reloads the website 2) you could get this value with javascript and load the saved page (window.location.href = 'http://website.website.com/YourSavedPage').
If you don't want that redirection every time the user visits the home page of website 1, you could think about creating a own page to redirect the user to the last opened page and to open that page once, when the iframe is loaded.
It seems like the targetOrigin (second argument of postMessage) may simply not match. Do not forget that the protocol, host & port must all be an exact match.
From the markup you posted, the iframe src domain is http://website.website.com while the parent domain is https://example.com.
If you wish for http://website.website.com to communicate it's URL to https://example.com then posting a message from the iframe should read:
window.parent.postMessage(window.location.pathname, 'https://example.com');
To make sure that the targetOrigin filter is not what's causing communication issues you can also use * for testing.
It seems that you are doing the opposite in your example (passing source domain instead of target domain) and it's also very misleading that you use "website 1" to reference the embedded site and "website 2" to reference the parent site in your explanation: I would expect the opposite.
The code samples with http://website.website.com and https://example.com doesn't work because there are on different URI schemes. One is http and another is https.
So, they have to be on the same HTTP protocol for this to work(either both http or both https).
In my example, I am using parent window URL as https://parent.example.com and iframe URL as https://child.somesite.com.
In iframe Site Code:
When the iframe site loads, we are going to send a postMessage() to the parent site about the current URL by assigning event listeners using addEventListenerto anchor tags, whenever they are clicked.
So, when an anchor tag is clicked, we prevent the default flow of route, send a message about current URL to the parent window and set current window href to the anchor's href.
Code:
var a_tags = document.getElementsByTagName('a');
for(var i=0;i<a_tags.length;++i){
a_tags[i].addEventListener('click',function(event){
event.preventDefault();
var current_href = this.getAttribute('href');
var new_location = current_href.match(/^http(s)?:\/\/.+$/) !== null ? current_href : window.location.origin + current_href;// be careful about leading '/' when dealing with relative URLs.
window.parent.postMessage(new_location,'https://parent.example.com');
window.location.href = new_location;
});
}
In parent window code:
Here, we will just attach an event listener to message event and check if the event was fired from our child site itself using the referrer present in event.origin.
If it's not, we return. If it is, we update our localStorage and set the URL received to the iframe_url key.
While refreshing the page, we first check if localStorage has this key set or not. If not, we load iframe as is, else, we load the URL we have in our storage by setting it's src attribute.
Note that we make an iframe element from javascript to avoid attaching separate event handlers to deal with it's src when requested on a new tab in the window.
Code:
const IFRAME_SITE_DOMAIN = 'https://child.somesite.com';
window.addEventListener('message',function(event){
if(event.origin !== IFRAME_SITE_DOMAIN) return;
localStorage.setItem('iframe_url',event.data);
});
var iframe = document.createElement('iframe');
if(localStorage.getItem('iframe_url') === null){
iframe.setAttribute('src',IFRAME_SITE_DOMAIN);
}else{
iframe.setAttribute('src',localStorage.getItem('iframe_url'));
}
iframe.setAttribute('height','500');
iframe.setAttribute('width','500');
document.body.append(iframe);
Sharable Links:
We make a button and span for sharable user actions like so.
Code:
<button id='share_resource_state'>Share Link</button>
<span id='share_url'></span>
Now, we add the iframe's current URL in URL fragments(characters after #). Since we are adding this in a fragment, we need not worry about it's effect on server side of parent site as it is never sent to the server and plays a role purely on the client's browser.
We convert the iframe's URL to base64 using btoa() while sharing and decode it using atob() when requested on a new tab or window.
This changes the current code on parent site(main window) a bit like so.
Code:
const IFRAME_SITE_DOMAIN = 'https://child.somesite.com';
window.addEventListener('message',function(event){
if(event.origin !== IFRAME_SITE_DOMAIN) return;
localStorage.setItem('iframe_url',event.data);
});
var iframe = document.createElement('iframe');
if(localStorage.getItem('iframe_url') === null){
if(window.location.hash != ''){
try{
var decoded_string = atob(window.location.hash.substring(1));// to remove the # from the fragment and get the base64 encoded data.
if(decoded_string.indexOf('iframe_url=') !== -1){
iframe.setAttribute('src',decoded_string.split('=')[1]);// we split the string based on '=' and assign the iframe URL which was set at the time of sharing
}else{
iframe.setAttribute('src',IFRAME_SITE_DOMAIN); // we don't deal with the fragment at all since it isn't encoded for our iframe purpose.
}
}catch(e){
iframe.setAttribute('src',IFRAME_SITE_DOMAIN); // we don't deal with the fragment at all.
}
}else{
iframe.setAttribute('src',IFRAME_SITE_DOMAIN); // we set URL as is.
}
}else{
iframe.setAttribute('src',localStorage.getItem('iframe_url'));
}
iframe.setAttribute('height','500');
iframe.setAttribute('width','500');
document.body.append(iframe);
document.getElementById('share_resource_state').addEventListener('click',function(){
var iframe_sharable_url = localStorage.getItem('iframe_url') === null ? IFRAME_SITE_DOMAIN : localStorage.getItem('iframe_url');
document.getElementById('share_url').innerHTML = window.location.href.split('#')[0] + '#' + btoa('iframe_url=' + iframe_sharable_url);
});
Some pointers before we start, whenever you have a problem it is always good to check the following basics first.
Basic problem solving
Make a bare minimum proof of concept that only shows the problem and nothing else. Remove all extra markup, styling and code.
Make sure your libraries are up to date (you are using jquery 1.11.3 instead of 3.3.1).
Follow standards, conventions, best practices if you are swimming upstream you only make it harder on yourself.
Best practices used in this answer
You are advised to follow these, they are called best practices because they make life easier not harder.
script tags go at the bottom of the page
encapsulate all your own scripts with a self executing function block in order not to pollute the global namespace
using the popular and well known $ as the jQuery reference so that everyone understands each other
using use strict javascript directive will warn about problem areas in advance
terminology
parent - refers to the main document in the browser window with the iframe markup
child - refers to the document inside the parent's iframe
Cross frame access - the answer
Access child document from the parent document
To access the child document from the parent iframe we use iframe.contentWindow. Once we have the iframe window we gain access to the child document with iframe.contentWindow.document
Access parent document from the child document
To access the parent iframe from the child document we use window.frameElement. Once we have the parent iframe element we can access the parent document with window.frameElement.ownerDocument.
The basic example
Unfortunately your examples are so convoluted with numerous problems outside the scope of this question that I was compelled to re-create these pages in order to facilitate as examples.
These examples show retrieving both the child and parent location from either the child or the parent and visa versa.
The Parent - test.html
Notice the span ids parentOut and childOut which gets populated with jQuery.
<!DOCTYPE html>
<html>
<head>
<title>Website.com</title>
</head>
<body>
<h1>Parent page</h1>
<span>Parent location: <span id="parentOut"></span></span><br>
<span>Child location: <span id="childOut"></span></span><br>
<div id="wrapper">
<iframe src="test_child.html" id="#iFrameContainer" width="100%" height="300"></iframe>
</div>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script>
// script encapsulation
(function ($) { "use strict";
// jQuery ready
$(function() {
$('#parentOut').text(document.location);
$('#childOut').text($('iframe')[0].contentWindow.document.location);
// the iframe by tag name
console.log($('iframe')[0]);
// the iframe by id
console.log($('#iFrameContainer')[0]);
// the iframe window
console.log($('iframe')[0].contentWindow);
// the child document
console.log($('iframe')[0].contentWindow.document);
});
})(jQuery);
</script>
</body>
</html>
The Child - test_child.html
Notice the span ids parentOut and childOut which gets populated with jQuery. There are also several hyperlinks of pages that WON'T work, see topic Security policies.
<!DOCTYPE html>
<html>
<head>
<title>Website.com</title>
</head>
<body>
<h1>Child page</h1>
<span>Child location: <span id="childOut"></span></span><br>
<span>Parent location: <span id="parentOut"></span></span><br>
<h3>Some child pages that DON'T work</h3>
SecurityError: Protocols, domains, and ports must match.<br>
SecurityError: Protocols must match.<br>
X-Frame-Options SAMEORIGIN<br>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script>
// script encapsulation
(function ($) { "use strict";
// jQuery ready
$(function() {
$('#childOut').text(document.location);
$('#parentOut').text(window.frameElement.ownerDocument.location);
// parent iframe
console.log(window.frameElement);
// parent document
console.log(window.frameElement.ownerDocument);
});
})(jQuery);
</script>
</body>
</html>
Being notified of child location changes
To be notified of location changes on the child document we can use the events onload or onloadstart to notify the parent.
$(document).on('load' function (event) {
$(window.frameElement.ownerDocument).append($('<p>').text('The location changed to:'+this.location);
});
Security policies
As we can see this functionality is quite powerful as it completely exposes both the parent and child documents to each other and visa versa. Because this allows you access to manipulate the content there are security policies in place to prevent us from manipulating the integrity of content that we do not own.
Protocols, domains, and ports must match
There is slightly different wording for similar errors but they all boil down to the child page must have the same domain name, same port and use the same protocol as the parent or access is blocked. The first two examples on the child page will return these errors respectively.
SecurityError: Blocked a frame with origin "http://127.0.0.1:1221" from accessing a frame with origin "http://my.umt.edu". Protocols, domains, and ports must match.
SecurityError: Blocked a frame with origin "http://127.0.0.1:1221" from accessing a frame with origin "https://en.wikipedia.org". The frame requesting access has a protocol of "http", the frame being accessed has a protocol of "https". Protocols must match.
These pages are allowed to be viewed in an iframe but if and only if the children are located at http://127.0.0.1:1221 (in my case) will this functionality be allowed.
Even further security
We can also completely prevent our sites from being viewed in an iframe. By means of the X-Frame-Options http response header, if configured with SAMEORIGIN the browser will refuse the page from being loaded in the frame. See last example on child page.
Conclusion
It is much simpler to find out exactly what the problem is if we set our project aside and start again with only the problem pieces. This also makes it much easier for someone to assist and provide a useful answer.
From what I understand of your use case, what you want to do is not allowed. You can freely make use of frames on your own site with your own pages but it is not allowed to manipulate someone else's content.
nJoy!
Just a though, based on the assumption that you can access and edit to the second website yet the server does not support PHP or any other programming/scripting language and you're stuck with HTML and Javascript:
In the parent PHP page which you are embedding the iframe into, you could call the iframe with an added parameter as shown bellow:
<iframe src="http://website.website.com/example.html?parent=<?=$_SERVER['HTTP_REFERER'];?>"></iframe>
Then in the child html page you can catch the parameter passed with the GET method with JavaScript or jQuery and use it for your purpose of determining the page, as bellow:
<script>
$(document).ready(function(){
var urlParams = new URLSearchParams(window.location.search);
var parentPage = urlParams.get('parent'); //which will store "https://example.com" in the variable. Now that you have the parent page URL you can manipulate it.
});
</script>
Even if you can't edit the html, you can inject JavaScript and HTML to the DOM of the iframe page through parent page and have it immediately run by declaring it within a jQuery function like so:
(function() {
var urlParams = new URLSearchParams(window.location.search);
var parentPage = urlParams.get('parent');
})();
I hope this made at least a little bit of sense, and can be helpful in any way. Good luck with your quest.
Cheers!
1, you need iframe show the same url even after reload
2, iframe and parent cross origin
3, you can inject js in iframe pages
4, parent page fully in control
check out https://github.com/postor/iframe-url-remember
npm i && npm run start and visit http://localhost:3000
postMessage works, I will explain in detail later, I have to catch a bus
I use node to serve static and mimic cross origin, so you can use nginx apache or php serve to host the public folder, and use lan IP and localhost mimic cross origin, you may need to modify some src
public/js/index.js is for parent page
window.addEventListener("message", receiveMessage, false);
function receiveMessage(event) {
console.log(event)
localStorage.setItem('iframesrc', event.data)
}
var src = localStorage.getItem('iframesrc')
src && (document.getElementById('iframe').src = src)
1.listen to message event, whenever new url comes write it into localStorage
2.on page load, read url from localStorage and modify src of iframe
public/js/iframe.js for the pages inside iframe
window.parent.postMessage(location.href, '*');
1.on page load, send url to parent page
it's easy and working
you can use cookie instead of localstorage then you can use php update iframe src before sending to client browser
or php session, you may need to trigger an ajax to notify server whenever url change
You could use a tracking pixel and pass the current path of the iframe as parameter:
var pathname = window.location.pathname;
var d = new Date();
var imageUrl = 'http://www.example.com/trackingpixel.php?path='
+ pathname + '&time=' + d.getTime();
var img = document.createElement('img');
img.src = imageUrl;
document.body.appendChild(img);
And in the parent domain, create the route trackingpixel.php and save the current path in the session:
if( !isset($_SESSION['time']) || ($_GET['time'] > $_SESSION['time'])) {
$_SESSION['time'] = $_GET['time'];
$_SESSION['path'] = $_GET['path'];
}
Then when you reload the page, you can get the path from the session:
if(isset($_SESSION['path'])) {
$iframeUrl = $_SESSION['path'];
}
else {
$iframeUrl = 'http://website.website.com';
}
Note that these is a slight chance this is not going to work if the reload is executed before the tracking pixel from the previous load.
PS: Nowadays ad block extensions are quite popular and they may prevent the pixel from "firing up", I would advice to test whether the pixel works with some of the popular extensions.

Sandboxing content in an IFRAME, on the client side

The issue
I have some javascript content that I want to "sandbox" into an iframe:
<script type="text/javascript">
doSomethingPotentiallyMalicious( // ideally, i want this to be able to run...
top.document.getElementById('sensitive_information') // ...but want this to fail due to cross-domain permissions
);
</script>
The catch is, due to the nature of our web application, I need to do this inline on the parent page that contains the iframe, and I need to do this in a cross-browser compatible way.
Data URL...almost but not quite
I was able to get the desired effect in Chrome by setting content in the iframe via a data url:
<iframe id="sandbox" src="data:text/html;charset=utf-8,%3Cscript%20type%3D%22text%2Fjavascript%22%3E%0A%20%20%20%20doSomethingPotentiallyMalicious(%20%2F%2F%20ideally%2C%20i%20want%20this%20to%20be%20able%20to%20run...%0A%20%20%20%20%20%20%20%20top.document.getElementById('sensitive_information')%20%2F%2F%20...but%20want%20this%20to%20fail%20due%20to%20cross-domain%20permissions%0A%20%20%20%20)%3B%0A%3C%2Fscript%3E"></iframe>
However, data url support is spotty and this needs to work cross-browser.
Document.write gets the content there, but lacks cross-domain security
I can have the unsafe content in a javascript escaped string, and then write it as the content of an iframe:
<iframe id="sandbox" src="http://google.com/"></iframe>
<script>
var unsafeContent = '\x3Cscript\x20type\x3D\x22text\x2Fjavascript\x22\x3E\x0A\x20\x20\x20\x20doSomethingPotentiallyMalicious\x28\x20\x2F\x2F\x20ideally,\x20i\x20want\x20this\x20to\x20be\x20able\x20to\x20run...\x0A\x20\x20\x20\x20\x20\x20\x20\x20top.document.getElementById\x28\x27sensitive_information\x27\x29\x20\x2F\x2F\x20...but\x20want\x20this\x20to\x20fail\x20due\x20to\x20cross\x2Ddomain\x20permissions\x0A\x20\x20\x20\x20\x29\x3B\x0A\x3C\x2Fscript\x3E\x0A\x0A';
var sandbox = document.getElementById('sandbox');
sandbox = (sandbox.contentWindow) ? sandbox.contentWindow : (sandbox.contentDocument.document) ? sandbox.contentDocument.document : sandbox.contentDocument;
sandbox.document.open();
sandbox.document.write(unsafeContent);
sandbox.document.close();
</script>
The problem with this is, once I write that content to the iframe, the cross-domain security is apparently no longer there (meaning that doSomethingPotentiallyMalicious function has access to the everything in the parent window).
Document.write + Document.domain doesn't seem to get us there, either
I even tried changing the document.domain (by removing the left-most domain so "www.example.com" becomes "example.com") per this previous SO post, but this doesn't seem to enforce a cross-domain policy, either:
<iframe id="sandbox" src="http://google.com/"></iframe>
<script>
// prepended to unsafeContent: document.domain = document.domain.replace(/^[\w-]+\./,'');
var unsafeContent = '\x3Cscript\x20type\x3D\x22text\x2Fjavascript\x22\x3E\x0A\x20\x20\x20\x20document.domain\x20\x3D\x20document.domain.replace\x28\x2F\x5E\x5B\x5Cw\x2D\x5D\x2B\x5C.\x2F,\x27\x27\x29\x3B\x0A\x20\x20\x20\x20doSomethingPotentiallyMalicious\x28\x20\x2F\x2F\x20ideally,\x20i\x20want\x20this\x20to\x20be\x20able\x20to\x20run...\x0A\x20\x20\x20\x20\x20\x20\x20\x20top.document.getElementById\x28\x27sensitive_information\x27\x29\x20\x2F\x2F\x20...but\x20want\x20this\x20to\x20fail\x20due\x20to\x20cross\x2Ddomain\x20permissions\x0A\x20\x20\x20\x20\x29\x3B\x0A\x3C\x2Fscript\x3E\x0A\x0A';
var sandbox = document.getElementById('sandbox');
sandbox = (sandbox.contentWindow) ? sandbox.contentWindow : (sandbox.contentDocument.document) ? sandbox.contentDocument.document : sandbox.contentDocument;
sandbox.document.open();
sandbox.document.write(unsafeContent);
sandbox.document.close();
</script>
Is what I'm trying to do even technically feasible at this point?
I wonder if window.postMessage would work. You could set up an iframe that evals the first message it receives to inject javascript into it. According to the site I linked, it works in Firefox, IE8+, Opera, Safari, and Chrome. Hopefully that's cross-browser enough for you. Mobile devices might have issues with it.

Passing Messages from iFrame Across all Browsers

I have an embed-able iframe that will be used on 3rd party sites. It has several forms to fill out, and at the end must inform the parent page that it is done.
In other words, the iframe needs to pass a message to it's parent when a button is clicked.
After wading through oceans of "No, cross-domain policy is a jerk" stuff, I found window.postMessage, part of the HTML5 Draft Specification.
Basically, you place the following JavaScript in your page to capture a message from the iframe:
window.addEventListener('message', goToThing, false);
function goToThing(event) {
//check the origin, to make sure it comes from a trusted source.
if(event.origin !== 'http://localhost')
return;
//the event.data should be the id, a number.
//if it is, got to the page, using the id.
if(!isNaN(event.data))
window.location.href = 'http://localhost/somepage/' + event.data;
}
Then in the iframe, have some JavaScript that sends a message to the parent:
$('form').submit(function(){
parent.postMessage(someId, '*');
});
Awesome, right? Only problem is it doesn't seem to work in any version of IE. So, my question is this: Given that I need to pass a message from an iframe to it's parent (both of which I control), is there a method I can use that will work across any (>IE6) browser?
In IE you should use
attachEvent("onmessage", postMessageListener, false);
instead of
addEventListener("message", postMessageListener, false);
The main work-around I've seen used involves setting a hash value on the parent window and detecting the hash value in the parent, parsing the hash value to obtain the data and do whatever you want. Here's one example of doing that: http://www.onlineaspect.com/2010/01/15/backwards-compatible-postmessage/. There are more options via Google like this one: http://easyxdm.net/wp/.
This is way simpler than that.
You say you control both the parent and the content of the frame you can set up two way
communication in javascript.
All you need is
yourframename.document.getElementById('idofsomethinginttheframe')
And then from inside the frame address anything outside it with
parent.document

Detecting the onload event of a window opened with window.open

window.popup = window.open($(this).attr('href'), 'Ad', 'left=20,top=20,width=500,height=500,toolbar=1,resizable=0');
$(window.popup).onload = function()
{
alert("Popup has loaded a page");
};
This doesn't work in any browser I've tried it with (IE, Firefox, Chrome). How can I detect when a page is loaded in the window (like an iframe onload)?
var myPopup = window.open(...);
myPopup.addEventListener('load', myFunction, false);
If you care about IE, use the following as the second line instead:
myPopup[myPopup.addEventListener ? 'addEventListener' : 'attachEvent'](
(myPopup.attachEvent ? 'on' : '') + 'load', myFunction, false
);
As you can see, supporting IE is quite cumbersome and should be avoided if possible. I mean, if you need to support IE because of your audience, by all means, do so.
If the pop-up's document is from a different domain, this is simply not possible.
Update April 2015: I was wrong about this: if you own both domains, you can use window.postMessage and the message event in pretty much all browsers that are relevant today.
If not, there's still no way you'll be able to make this work cross-browser without some help from the document being loaded into the pop-up. You need to be able to detect a change in the pop-up that occurs once it has loaded, which could be a variable that JavaScript in the pop-up page sets when it handles its own load event, or if you have some control of it you could add a call to a function in the opener.
As noted at Detecting the onload event of a window opened with window.open, the following solution is ideal:
/* Internet Explorer will throw an error on one of the two statements, Firefox on the other one of the two. */
(function(ow) {
ow.addEventListener("load", function() { alert("loaded"); }, false);
ow.attachEvent("onload", function() { alert("loaded"); }, false);
})(window.open(prompt("Where are you going today?", location.href), "snapDown"));
Other comments and answers perpetrate several erroneous misconceptions as explained below.
The following script demonstrates the fickleness of defining onload. Apply the script to a "fast loading" location for the window being opened, such as one with the file: scheme and compare this to a "slow" location to see the problem: it is possible to see either onload message or none at all (by reloading a loaded page all 3 variations can be seen). It is also assumed that the page being loaded itself does not define an onload event which would compound the problem.
The onload definitions are evidently not "inside pop-up document markup":
var popup = window.open(location.href, "snapDown");
popup.onload = function() { alert("message one"); };
alert("message 1 maybe too soon\n" + popup.onload);
popup.onload = function() { alert("message two"); };
alert("message 2 maybe too late\n" + popup.onload);
What you can do:
open a window with a "foreign" URL
on that window's address bar enter a javascript: URI -- the code will run with the same privileges as the domain of the "foreign" URL
The javascript: URI may need to be bookmarked if typing it in the address bar has no effect (may be the case with some browsers released around 2012)
Thus any page, well almost, irregardless of origin, can be modified like:
if(confirm("wipe out links & anchors?\n" + document.body.innerHTML))
void(document.body.innerHTML=document.body.innerHTML.replace(/<a /g,"< a "))
Well, almost:
jar:file:///usr/lib/firefox/omni.ja!/chrome/toolkit/content/global/aboutSupport.xhtml, Mozilla Firefox's troubleshooting page and other Jar archives are exceptions.
As another example, to routinely disable Google's usurping of target hits, change its rwt function with the following URI:
javascript: void(rwt = function(unusurpURL) { return unusurpURL; })
(Optionally Bookmark the above as e.g. "Spay Google" ("neutralize Google"?)
This bookmark is then clicked before any Google hits are clicked, so bookmarks of any of those hits are clean and not the mongrelized perverted aberrations that Google made of them.
Tests done with Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:11.0) Gecko/20100101 Firefox/11.0 UA string.
It should be noted that addEventListener in Firefox only has a non-standard fourth, boolean parameter, which if true allows untrusted content triggers to be instantiated for foreign pages.
Reference:
element.addEventListener | Document Object Model (DOM) | MDN:
Interaction between privileged and non-privileged pages | Code snippets | MDN:
This did the trick for me; full example:
HTML:
Click for my popup on same domain
Javascript:
(function(){
var doc = document;
jQuery('.import').click(function(e){
e.preventDefault();
window.popup = window.open(jQuery(this).attr('href'), 'importwindow', 'width=500, height=200, top=100, left=200, toolbar=1');
window.popup.onload = function() {
window.popup.onbeforeunload = function(){
doc.location.reload(true); //will refresh page after popup close
}
}
});
})();
onload event handler must be inside popup's HTML <body> markup.
First of all, when your first initial window is loaded, it is cached. Therefore, when creating a new window from the first window, the contents of the new window are not loaded from the server, but are loaded from the cache. Consequently, no onload event occurs when you create the new window.
However, in this case, an onpageshow event occurs. It always occurs after the onload event and even when the page is loaded from cache. Plus, it now supported by all major browsers.
window.popup = window.open($(this).attr('href'), 'Ad', 'left=20,top=20,width=500,height=500,toolbar=1,resizable=0');
$(window.popup).onpageshow = function() {
alert("Popup has loaded a page");
};
The w3school website elaborates more on this:
The onpageshow event is similar to the onload event, except that it occurs after the onload event when the page first loads. Also, the onpageshow event occurs every time the page is loaded, whereas the onload event does not occur when the page is loaded from the cache.
The core problem seems to be you are opening a window to show a page whose content is already cached in the browser. Therefore no loading happens and therefore no load-event happens.
One possibility could be to use the 'pageshow' -event instead, as described in:
https://support.microsoft.com/en-us/help/3011939/onload-event-does-not-occur-when-clicking-the-back-button-to-a-previou
Simple solution:
new_window = window.open(...);
new_window.document.write('<body onload="console.log(1);console.log(2);></body>');

In XUL, how do I know a browser-tag has finished loading?

I'm developing a firefox extension based on this tutorial which is a FF 2.0 extension (second part of the tutorial is at this url)
The main thing that is important is that it uses
<iframe id="contentview" src="http://www.ibm.com/developerworks/web" flex="2"/>
In the backend code, when clicking the GO button, this happens:
contentview.contentDocument.location.href = urlbox.value;
//Use Firefox XPath to get the raw text of the document
var doctext = contentview.contentDocument.evaluate(
"string(.)", document, null, XPathResult.STRING_TYPE, null).stringValue;
I get an error with the xpath, but that's not my question. The issue I have with FF 3.0 is that the contentDocument value refers to the old site loaded, not to the one loaded by the href-change.
So my question is: how can I create a similar window, but be notified someone when the loaded document is complete, so I can access its DOM?
Updated:
first you need to handle the load event of the window then you add an event listener to the iframe element
window.addEventListener("load",Listen,false);
function Listen()
{
var frame = document.getElementById("contentview");
frame.addEventListener("DOMContentLoaded", DomLoadedEventHandler, true);
}
function DomLoadedEventHandler() {
var frame = document.getElementById("contentview");
alert(frame.contentDocument.location.href);
}
replace "DomLoadedEventHandler" with your event handler name.
I recommend that you take a look at the official site of Mozilla to learn everything about Firefox extensions
http://developer.mozilla.com

Categories