Some websites have code to "break out" of IFRAME enclosures, meaning that if a page A is loaded as an IFRAME inside an parent page P some Javascript in A redirects the outer window to A.
Typically this Javascript looks something like this:
<script type="text/javascript">
if (top.location.href != self.location.href)
top.location.href = self.location.href;
</script>
My question is: As the author of the parent page P and not being the author of the inner page A, how can I prevent A from doing this break-out?
P.S. It seems to me like it ought to be a cross-site security violation, but it isn't.
With HTML5 the iframe sandbox attribute was added. At the time of writing this works on Chrome, Safari, Firefox and recent versions of IE and Opera but does pretty much what you want:
<iframe src="url" sandbox="allow-forms allow-scripts"></iframe>
If you want to allow top-level redirects specify sandbox="allow-top-navigation".
I use the sandbox attribute on the iframe element:
allow-forms allow form submission
allow-popups allows popups
allow-pointer-lock allows pointer lock
allow-same-origin allows the document to maintain its origin
allow-scripts allows JavaScript execution, and also allows features to trigger automatically
allow-top-navigation allows the document to break out of the frame by navigating the top-level window
Top navigation is what you want to prevent, so leave that out and it will not be allowed. Anything left out will be blocked
ex.
<iframe sandbox="allow-same-origin allow-scripts allow-popups allow-forms" src="http://www.example.com"</iframe>
Try using the onbeforeunload property, which will let the user choose whether he wants to navigate away from the page.
Example: https://developer.mozilla.org/en-US/docs/Web/API/Window.onbeforeunload
In HTML5 you can use sandbox property. Please see Pankrat's answer below.
http://www.html5rocks.com/en/tutorials/security/sandboxed-iframes/
After reading the w3.org spec. I found the sandbox property.
You can set sandbox="", which prevents the iframe from redirecting. That being said it won't redirect the iframe either. You will lose the click essentially.
Example here: http://jsfiddle.net/ppkzS/1/
Example without sandbox: http://jsfiddle.net/ppkzS/
I know it has been a long time since question was done but here is my improved version it will wait 500ms for any subsequent call only when the iframe is loaded.
<script type="text/javasript">
var prevent_bust = false ;
var from_loading_204 = false;
var frame_loading = false;
var prevent_bust_timer = 0;
var primer = true;
window.onbeforeunload = function(event) {
prevent_bust = !from_loading_204 && frame_loading;
if(from_loading_204)from_loading_204 = false;
if(prevent_bust){
prevent_bust_timer=500;
}
}
function frameLoad(){
if(!primer){
from_loading_204 = true;
window.top.location = '/?204';
prevent_bust = false;
frame_loading = true;
prevent_bust_timer=1000;
}else{
primer = false;
}
}
setInterval(function() {
if (prevent_bust_timer>0) {
if(prevent_bust){
from_loading_204 = true;
window.top.location = '/?204';
prevent_bust = false;
}else if(prevent_bust_timer == 1){
frame_loading = false;
prevent_bust = false;
from_loading_204 = false;
prevent_bust_timer == 0;
}
}
prevent_bust_timer--;
if(prevent_bust_timer==-100) {
prevent_bust_timer = 0;
}
}, 1);
</script>
and onload="frameLoad()" and onreadystatechange="frameLoad();" must be added to the frame or iframe.
Since the page you load inside the iframe can execute the "break out" code with a setInterval, onbeforeunload might not be that practical, since it could flud the user with 'Are you sure you want to leave?' dialogs.
There is also the iframe security attribute which only works on IE & Opera
:(
In my case I want the user to visit the inner page so that server will see their ip as a visitor. If I use the php proxy technique I think that the inner page will see my server ip as a visitor so it is not good. The only solution I got so far is wilth onbeforeunload.
Put this on your page:
<script type="text/javascript">
window.onbeforeunload = function () {
return "This will end your session";
}
</script>
This works both in firefox and ie, thats what I tested for.
you will find versions using something like evt.return(whatever) crap... that doesn't work in firefox.
try:
<iframe sandbox=" "></iframe>
By doing so you'd be able to control any action of the framed page, which you cannot. Same-domain origin policy applies.
Related
I have an iframe of a certain page from a site that I'm using, but I don't want all the parts of that page to be displayed with the iframe. Particularly, there's a navigation sidebar on the page that I don't want to be in the iframe. I'm trying to achieve this with the javascript seen below, but I can't quite figure it out.
<iframe width="800" height="800" src="scores/new?site_id=193">
<script>
var element = document.getElementById("sidebar-wrapper");
element.parentNode.removeChild(element);
</script>
</iframe>
For security reasons you can't run javascript through iframes. There are some exceptions if you're on the same domain but for the most part you should really avoid it.
If the iframe isn't a site you can control then there's pretty much nothing you can do. If you do control the other site and it's a different domain you might be able to work with the postMessage functions.
Edit: Check out the docs that Mozilla has up here https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage
You'd need to create a listener on the inside that handles a message and hides your sidebar. Then on the parent send a message to the iframe to trigger that function.
Parent:
var iframe = document.getElementById('#iframeID');
iframe.contentWindow.postMessage('iframeTrigger');
Iframe:
window.addEventListener('iframeTrigger', hideSidebar);
function hideSidebar() {
//do stuff
}
You can insert a control in the iframed page
//inside the iframed page
var iframe = (function() {
try {
return window.self !== window.top;
} catch (e) {
return true;
}
})();
if(iframe === true) {
var element = document.getElementById("sidebar-wrapper");
element.parentNode.removeChild(element);
}
Hope this could suit your need.
This should work theoretically, and it works in console. But this doesn't work in the HTML, although you are trying it from the same domain, because of security reasons. I just wanted to tell my view and I tried this:
<iframe src="http://output.jsbin.com/figujeyiyo" frameborder="0" id="ifrm">
Sorry, iframes not supported.
</iframe>
<script>
console.log(document.getElementById("ifrm").contentDocument.documentElement.getElementsByTagName("div"));
e = document.getElementById("ifrm").contentDocument.documentElement.getElementsByTagName("div")[0];
console.log(e);
e.parentNode.removeChild(element);
</script>
You need to execute the code when the page loads, you can do it like this:
document.addEventListener('DOMContentLoaded', function() {
var element = document.getElementById("sidebar-wrapper");
element.parentNode.removeChild(element);
});
I'm currently writing a little program that generates an html file and opens it with the default browser to start multiple downloads.
I don't want to open a tab/window for every download, so creating hidden iframes for the downloads seemed like a good solution.
I'm using onload on the iframes to find out if the download prompts for each download have shown up yet. This approach seems to be very unreliable in the Internet Explorer though.
So I'm wondering if there is there a way to fix this or maybe a better approach?
(Without libraries please.)
Here is my html/js code:
<!DOCTYPE html>
<!-- saved from url=(0016)http://localhost -->
<html><head>
<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
<meta content="utf-8" http-equiv="encoding">
<title>Downloads</title>
<script>
"use strict";
var downloadsInfo = {
"http://download-installer.cdn.mozilla.net/pub/firefox/releases/26.0/win32/en-US/Firefox%20Setup%2026.0.exe":"Status: Connecting",
"http://download.piriform.com/ccsetup410.exe":"Status: Connecting"
};
var i = 0;
var iv = setInterval(function() {
i = ++i % 4;
var j = 0;
var finished = true;
for (var key in downloadsInfo) {
var value = downloadsInfo[key];
if (value != "Status: Download Started!") {
value = value+Array(i+1).join(".");
finished = false;
}
document.getElementsByTagName("div")[j].innerHTML = key+"<br/>"+value;
j = j+1;
}
if (finished) {
alert('Done! You can close this window/tab now.');
clearInterval(iv);
}
}, 800);
</script>
</head><body>
<h3>Please wait for your downloads to start and do not reload this site.</h3>
<div></div> <br/><br/>
<div></div> <br/><br/>
<iframe src="http://download-installer.cdn.mozilla.net/pub/firefox/releases/26.0/win32/en-US/Firefox%20Setup%2026.0.exe" onload="downloadsInfo['http://download-installer.cdn.mozilla.net/pub/firefox/releases/26.0/win32/en-US/Firefox%20Setup%2026.0.exe'] = 'Status: Download Started!';" style="display:none"></iframe>
<iframe src="http://download.piriform.com/ccsetup410.exe" onload="downloadsInfo['http://download.piriform.com/ccsetup410.exe'] = 'Status: Download Started!';" style="display:none"></iframe>
</body></html>
Quite simply you can't know whether a native browser download started. Every browser has different ways this is handled, the user may set up his browser to prompt the location or he might just let it auto download to the Downloads folder (the default in most browsers nowadays). If he's prompting for a location he might cancel by mistake, yet your setup would still claim the download started. So, no, there is no way whatsoever to reliably inform the user that they can close a tab once all downloads are started/finished... provided that you use the native browser download mechanism.
The way to achieve this effect would be possibly by first downloading the file using Javascript (requiring you to have access to those files, hotlinking to third party files is of course not an option then). To see this in action try downloading a file from mega.nz. I was planning on writing up how to do this by hand, but there is already a nice (quite outdated) answer outlining this.
If the intention is only to ensure that the download has started you could implement a trigger on the back end to note when the file has been accessed. In it's simplest form this would look like:
Page download.html requests file.php?location=[...]&randomHash=1234
Once file.php is actually loaded it will set a flag in memory or the database that randomHash id 1234 has started.
file.php redirects the page with a 302 header to the actual file location.
download.html checks periodically using Ajax whether flag randomHash=1234 has been raised. If so it knows the download has started.
Indeed IE is reported to not always behave nicely with the onload event handler of iframes. There is an active bug tracker record opened.
The problem is discussed in a number of places around the web, and what seems to be the most reliable solution is to have an indirect download with nested iframes: the iframe loads a HTML file with an iframe that loads the file to download. The reason for that is that IE does not seem to like iframes that point to something else than HTML. So if you have the possibility to do that in your program:
For each file to download, generate a HTML file with a body that looks like this:
<iframe src="http://filetodownload.exe" style="display:none"></iframe>
Store this file in a temporary folder, e.g. C:\tmp\filetodownload.html
In your "master" generated HTML file, replace the iframe source with this intermediate file:
<iframe src="C:\tmp\filetodownload.html"
onload="downloadsInfo['http://filetodownload.exe']='Status: Download Started!';"
style="display:none"></iframe>
That may do the trick. But following IE's tradition, this could or could not work depending on the case...
If it does not work, some solutions that have proved useful include:
Put the onload handler in a function, and write in the definition of the iframe: onload="return theonloadfunction()" (even if the function does not return anything)
Instead of using the onload attribute, attach the event handler in javascript, like so:
iframe = document.getElementById("theiframeid")
iframe.attachEvent("onload", theonloadfunction);`
Note that attachEvent is for IE only. If you want to support other browsers you will have to detect it and use addEventListener for the non-IE cases.
Finally, you may try combinations of two or more of these solutions :)
<html>
<head>
<meta content = 'text/html;charset=utf-8' http-equiv = 'Content-Type'>
<meta content = 'utf-8' http-equiv = 'encoding'>
<script>
/*
First, I removed the setInterval(). Since you rely on the onload property we can aswell just check it on each onload.
Second, I changed your downloadsInfo to an object array.
Also be aware while testing, that some browsers cache your cancel/block choice and do not reask again for the same url.
Additionally firefox does not fire on frame downloads.
Furthermore the alert in your test might not show for overlapping or setting reasons.
*/
var downloadsInfo = [
{url: "http://download-installer.cdn.mozilla.net/pub/firefox/releases/26.0/win32/en-US/Firefox%20Setup%2026.0.exe", Status: "Connecting"},
{url: "http://download.piriform.com/ccsetup410.exe", Status: "Connecting"}
];
//IE has a problem in sometimes merely firing the onload propery once, which we bypass by dynamically creating them
//It is also less limited.
function iframeConnect(){
for(var i=0, j=downloadsInfo.length; i<j; i++){
var tF = document.createElement('iframe');
tF.arrayIndex = i; //For convenience
tF.style.display = 'none';
//Normal load event, working in ie8-11, chrome, safari
tF.onload = function(){
iframeExecuted(this.arrayIndex);
};
//Workaround for firefox, opera and some ie9
tF.addEventListener('DOMSubtreeModified', function(){
iframeExecuted(this.arrayIndex);
}, false);
document.body.appendChild(tF);
tF.src = downloadsInfo[i].url;
}
}
function iframeExecuted(i){
downloadsInfo[i].Status = 'Executed';
var tStatus = iframeFinished();
var tE = document.querySelector('h3');
if (tStatus.Done) tE.innerHTML = 'Finished'
else tE.innerHTML = 'Processed ' + tStatus.Processed + ' of ' + tStatus.Started;
}
function iframeFinished(){
for(var i=0, j=downloadsInfo.length; i<j; i++){
if (downloadsInfo[i].Status != 'Executed') break;
}
//Note that the Processed value is not accurate, yet it solves is testing purpose.
return {Done: (i == j), Processed: i, Started: j}
}
</script>
</head>
<body onload = 'iframeConnect()'>
<h3>Please wait for your downloads to start and do not reload this site.</h3>
</body>
</html>
Is there any way to resize a cross domain iframe according to its content, that would work in Firefox 3.6.10?
I thought that the postMessage command works, but a solution I found works in Firefox 12 but not in Firefox 3.6.10. Or maybe that's not the problem.
As I wrote in another question, youtube seems to be just embedding the content of the iframe in the page for the comment section, that's how it gets resized dynamically. And this would basically solve every iframe issue too, since the HTML would be embedded in the website's local HTML, and no same origin policy would block anything. When I asked about this, I didn't get answers though.
So thank you in advance, or if you don't like when people say that, I would be grateful if someone would helped.
And I CAN control the content inside the frame too!
(I need this for a script that makes youtube look like around 2012, so more people could be grateful too.)
You might try looking at this on GitHub.
https://github.com/davidjbradshaw/iframe-resizer
solution i found/modified:
in the page which contains the iframe:
<script type="text/javascript">
function resizeCrossDomainIframe(id, other_domain) {
var iframe = document.getElementById(id);
window.addEventListener('message', function(event) {
if (event.origin !== other_domain) return;
if (isNaN(event.data)) return;
var height = parseInt(event.data) + 32;
iframe.height = height + "px";
}, false);
}
in the iframe code:
<iframe id="my_iframe" onload="resizeCrossDomainIframe('my_iframe', '**whatever domain your iframe content is on**');"></iframe>
for continuous resizing, i put this in the page containing the iframe:
<script>
var interval = setInterval(function(){
resizeCrossDomainIframe('my_iframe', '**whatever domain your iframe content is on**');
}, 100);
</script>
on the iframe content page:
<script>
window.onload = function() {
window.parent.postMessage(document.body.scrollHeight, '**whatever domain your iframe containing page is on**');
}
</script>
and this for continuous resizing:
<script>
var interval2 = setInterval(function(){
window.parent.postMessage(document.body.scrollHeight, '**whatever domain your iframe containing page is on**');
}, 100);
</script>
CORRECTION: the first interval script isnt needed, since the eventlistener executes the function every time a message is sent to it. it would just cause slowness
As part of some XSS research I'm doing, I'm trying to spawn an iframe with a changed form action and then close the iframe after the victim has submitted their details. I have the below code so far:
<body onload="iframeEdit()">
<script>
function deleteIframe() {
var iframe = document.getElementById("myframe");
iframe.parentNode.removeChild(iframe);
}
function iframeEdit() {
var iframe = document.getElementById("myframe");
var innerDoc = iframe.contentDocument || iframe.contentWindow.document;
var form = innerDoc.getElementsByTagName("form");
form[0].action = "http://127.0.0.1/new/page.php";
form[0].setAttribute("onSubmit", "deleteIframe()");
}
</script>
<iframe id="myframe" src="http://127.0.0.1/iframe/page.php" height="250" width="300">
That code still posts the form details to the php page; however, it fails to close the iframe afterwards. I know the iframe delete code works, as if I try it on its own in it works as it should. So it's either to do with the logic flow of the script, or the setAttribute line isn't working as it should. Can anyone shed some light on where I'm going wrong? Thanks!
There may be a better way to do this, but a work around I found was to pause the deleteIframe function, via setTimeout(). I found as little as 5 milliseconds was enough for the form details to be successfully posted :)
function deleteIframeTimer(){
var t = setTimeout("deleteIframe()", 5);
}
function deleteIframe() {
var iframe = parent.document.getElementById("myframe");
iframe.parentNode.removeChild(iframe);
}
Many thanks to Jason for the parenttip!
This is just a guess without running your code
form[0].setAttribute("onSubmit", "parent.deleteIframe()");
My guess is that the iframe cannot access the parent window directly, so you'll need the parent
This is my code
<script>
var body = "dddddd"
var script = "<script>window.print();</scr'+'ipt>";
var newWin = $("#printf")[0].contentWindow.document;
newWin.open();
newWin.close();
$("body",newWin).append(body+script);
</script>
<iframe id="printf"></iframe>
This works but it prints the parent page, how do I get it to print just the iframe?
I would not expect that to work
try instead
window.frames["printf"].focus();
window.frames["printf"].print();
and use
<iframe id="printf" name="printf"></iframe>
Alternatively try good old
var newWin = window.frames["printf"];
newWin.document.write('<body onload="window.print()">dddd</body>');
newWin.document.close();
if jQuery cannot hack it
Live Demo
document.getElementById("printf").contentWindow.print();
Same origin policy applies.
Easy way (tested on ie7+, firefox, Chrome,safari ) would be this
//id is the id of the iframe
function printFrame(id) {
var frm = document.getElementById(id).contentWindow;
frm.focus();// focus on contentWindow is needed on some ie versions
frm.print();
return false;
}
an alternate option, which may or may not be suitable, but cleaner if it is:
If you always want to just print the iframe from the page, you can have a separate "#media print{}" stylesheet that hides everything besides the iframe. Then you can just print the page normally.
You can use this command:
document.getElementById('iframeid').contentWindow.print();
This command basically is the same as window.print(), but as the window we would like to print is in the iframe, we first need to obtain an instance of that window as a javascript object.
So, in reference to that iframe, we first obtain the iframe by using it's id, and then it's contentWindow returns a window(DOM) object. So, we are able to directly use the window.print() function on this object.
I had issues with all of the above solutions in IE8, have found a decent workaround that is tested in IE 8+9, Chrome, Safari and Firefox. For my situation i needed to print a report that was generated dynamically:
// create content of iframe
var content = '<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">'+
'<head><link href="/css/print.css" media="all" rel="stylesheet" type="text/css"></head>'+
'<body>(rest of body content)'+
'<script type="text/javascript">function printPage() { window.focus(); window.print();return; }</script>'+
'</body></html>';
Note the printPage() javascript method before the body close tag.
Next create the iframe and append it to the parent body so its contentWindow is available:
var newIframe = document.createElement('iframe');
newIframe.width = '0';
newIframe.height = '0';
newIframe.src = 'about:blank';
document.body.appendChild(newIframe);
Next set the content:
newIframe.contentWindow.contents = content;
newIframe.src = 'javascript:window["contents"]';
Here we are setting the dynamic content variable to the iframe's window object then invoking it via the javascript: scheme.
Finally to print; focus the iframe and call the javascript printPage() function within the iframe content:
newIframe.focus();
setTimeout(function() {
newIframe.contentWindow.printPage();
}, 200);
return;
The setTimeout is not necessarily needed, however if you're loading large amounts of content i found Chrome occasionally failed to print without it so this step is recommended. The alternative is to wrap 'newIframe.contentWindow.printPage();' in a try catch and place the setTimeout wrapped version in the catch block.
Hope this helps someone as i spent a lot of time finding a solution that worked well across multiple browsers. Thanks to SpareCycles.
EDIT:
Instead of using setTimeout to call the printPage function use the following:
newIframe.onload = function() {
newIframe.contentWindow.printPage();
}
At this time, there is no need for the script tag inside the iframe. This works for me (tested in Chrome, Firefox, IE11 and node-webkit 0.12):
<script>
window.onload = function() {
var body = 'dddddd';
var newWin = document.getElementById('printf').contentWindow;
newWin.document.write(body);
newWin.document.close(); //important!
newWin.focus(); //IE fix
newWin.print();
}
</script>
<iframe id="printf"></iframe>
Thanks to all answers, save my day.
If you are setting the contents of IFrame using javascript document.write() then you must close the document by newWin.document.close(); otherwise the following code will not work and print will print the contents of whole page instead of only the IFrame contents.
var frm = document.getElementById(id).contentWindow;
frm.focus();// focus on contentWindow is needed on some ie versions
frm.print();
I was stuck trying to implement this in typescript, all of the above would not work. I had to first cast the element in order for typescript to have access to the contentWindow.
let iframe = document.getElementById('frameId') as HTMLIFrameElement;
iframe.contentWindow.print();
Use this code for IE9 and above:
window.frames["printf"].focus();
window.frames["printf"].print();
For IE8:
window.frames[0].focus();
window.frames[0].print();
I am wondering what's your purpose of doing the iframe print.
I met a similar problem a moment ago: use chrome's print preview to generate a PDF file of a iframe.
Finally I solved my problem with a trick:
$('#print').click(function() {
$('#noniframe').hide(); // hide other elements
window.print(); // now, only the iframe left
$('#noniframe').show(); // show other elements again.
});