I have a page that has an iframe with external content. I don't want infinite loops in the external content to crash my whole page. Is there any way to get around this.
I tried to set something up where the parent postMessages the child iframe every so often and if the child Iframe doesn't respond for too long a time the parent changes the iframes src, but this doesn't seem to work. The parent's setTimeout functions no longer execute once the iframe starts looping. See my code here (note that it will crash your tab if you execute it, open the console before execution to view the logging):
<html>
<head>
</head>
<body>
<script type="text/javascript">
var scr = 'script';
var html = '<html><head><script>\n' +
' window.addEventListener("message", answer, false);' +
' function answer() { console.log("answered"); parent.postMessage(\'hi\', \'*\');}' +
' setTimeout("while(1){console.log(\'in loop\')};", 3000)' +
"</" + scr + "></head><body>IFRAME</body</html>";
var lastAnswer = (new Date()).getTime();
var iframe = document.createElement('iframe');
queryChild();
window.addEventListener("message", receive, false);
function receive() {
lastAnswer = (new Date()).getTime();
console.log('got answer');
}
function queryChild() {
console.log('querying');
if((new Date()).getTime() - lastAnswer > 5000) {
console.log('killing');
iframe.src = '';
} else if(iframe.contentWindow){
iframe.contentWindow.postMessage('hi', '*');
}
setTimeout(queryChild, 2000);
};
document.body.appendChild(iframe);
iframe.contentWindow.document.open();
iframe.contentWindow.document.write(html);
iframe.contentWindow.document.close();
</script>
</body>
</html>
Any suggestions on how to solve this problem?
I have a page that has an iframe with external content. I don't want infinite loops in the external content to crash my whole page. Is there any way to get around this.
I tried to set something up where the parent postMessages the child iframe every so often and if the child Iframe doesn't respond for too long a time the parent changes the iframes src, but this doesn't seem to work. The parent's setTimeout functions no longer execute once the iframe starts looping. See my code here (note that it will crash your tab if you execute it, open the console before execution to view the logging):
<html>
<head>
</head>
<body>
<script type="text/javascript">
var scr = 'script';
var html = '<html><head><script>\n' +
' window.addEventListener("message", answer, false);' +
' function answer() { console.log("answered"); parent.postMessage(\'hi\', \'*\');}' +
' setTimeout("while(1){console.log(\'in loop\')};", 3000)' +
"</" + scr + "></head><body>IFRAME</body</html>";
var lastAnswer = (new Date()).getTime();
var iframe = document.createElement('iframe');
queryChild();
window.addEventListener("message", receive, false);
function receive() {
lastAnswer = (new Date()).getTime();
console.log('got answer');
}
function queryChild() {
console.log('querying');
if((new Date()).getTime() - lastAnswer > 5000) {
console.log('killing');
iframe.src = '';
} else if(iframe.contentWindow){
iframe.contentWindow.postMessage('hi', '*');
}
setTimeout(queryChild, 2000);
};
document.body.appendChild(iframe);
iframe.contentWindow.document.open();
iframe.contentWindow.document.write(html);
iframe.contentWindow.document.close();
</script>
</body>
</html>
Any suggestions on how to solve this problem?
Share Improve this question asked Oct 8, 2012 at 21:32 asutherlandasutherland 2,9694 gold badges38 silver badges51 bronze badges4 Answers
Reset to default 6My experience with this kind of problem is that unless you can access the external code before feeding it to the iframe
(either as an URL or via the srcdoc attribute), the loop will pletely interrupt any JavaScript execution.
Whatever kind of timeout functionality you implement, it will not be called due to the iframe code execution consuming 100% resources until the browser reports a crash.
Your options are:
- Sanitize the code automatically before adding it to the
iframe
, which proves impractical since there are infinite ways to loop infinitely, and you will not be able to catch them all. You would have to write a scanner script that could detect infinite loops while not crashing in the course of scanning the code. - Use a sandboxing solution like Google Caja to sanitize the code. However, this will change the code structurally if not configured heavily.
- In case of an application that has capabilites of creating virtual environments and monitoring them, you could execute the iframe code (let's say on a virtual machine of sorts), check if the process locks up and use that oute to determine if you can safely set the
iframe.src
property to your code's URL. This might be the only solution that can guarantee some sort of guarantee that this code will not lock up immediately (however, there are many ways to have race conditions at some later point of execution, so there will not be a sure way to say it will never lock up the browser).
Summary: Unless you can find a way to test the code extensively before showing it in the iframe
, you can not guarantee that the iframe code will not lock up the browser tab.
It depends highly on the browser, some browser uses one thread for each page (or iframe), in this case your script cannot be executed until the iframe execution is over (the infinite loop). Some others have one thread per page (or iframe) and maybe you are able to do it.
What I'm sure is than if you expect to support enterprise browsers (like IE8) you can't.
This
console.log('killing');
iframe.src = '';
Will not kill the iframe
. according to same origin policy you can not manipulate external domains from your domain. Just changing the src
of an iframe
doesn't trigger a navigation in the iframe
. src
will change, but iframe
will not get navigated.
If your get the message from inner iframe
you should remove the iframe
from your document tree and insert a new one in document tree in order to kill the iframe using removeChild
document.body.removeChild(iframe);
document.body.appendChild(newiframe);
Look at this simple demonstration I've created here: http://jsbin./avodeb/1/
Try this:
<script>
document.getElementById('iframeID').onload= function() { //When the iframe loads quickly
clearTimeout(killerTimer); // Stop the Killer Timer
};
var killerTimer = setTimeout( function() {
document.getElementById("iframeID").setAttribute("src",""); //Otherwise, kill the iframe
}, 3000 );
</script>