Adding a variable to iframe's global scope - javascript

My index html:
<html>
<head>
<title>index</title>
<script src="jquery.js"></script>
</head>
<body>
<h1>Index</h1>
<hr/>
<iframe id="frame"></iframe>
<hr/>
<button id="btn">Click</button>
<script>
$(function(){
window.api = {
fun: function(){alert('index api')}
};
var frame = document.getElementById('frame');
frame.src = 'frame.html';
});
$('#btn').click(function(){
$('#frame').contents().find('body').css('backgroundColor', 'red');
$('#frame').contents().find('#btn').text('lol');
});
</script>
</body>
</html>
In the loaded frame.html the javascript can acces the api object via parent.api.
Is it possible to add an api object to frame.html window, so it is accessible via window.api there? (and not parent.api) (consider same origin and differnet origins)?
The above code does work (e.g. the #button changing the background and label), but I assume it's because all those documents are on my pc (same origin). Am I right?

Assuming you're not running into the same origin policy
var frame = document.getElementById('frame'); // get the frame
frame.addEventListener('load', function () { // only access when loaded
var win = frame.contentWindow; // get reference to iframe's `window`
win['foo'] = 'bar'; // set global (within iframe)
});
frame.src = 'frame.html'; // load up page in iframe
If the contents are on a different domain, it's not guaranteed that you will be able to access the <iframe>'s Window even with CORS set up.
The way functions and scope work means they are a bit more difficult to do in the right context, you'd have to either make them generic so it doesn't care or use new win.Function

Related

Object.defineProperty not changing property of element

I am trying to override the src property of all iframes in my application so their src property always gets set to "redirect.html" regardless of what value the HTML tag defines for it.
So far, I have come up with the following, but it doesn't seem to be applying to the DOM element:
<!doctype html>
<html>
<head>
<script>
var propertyDescriptorSrc = Object.getOwnPropertyDescriptor(HTMLIFrameElement.prototype, "src");
Object.defineProperty(HTMLIFrameElement.prototype, "src", {
get: function get_src() {
var val = propertyDescriptorSrc.get.call(this);
return "redirect.html";
},
set: function (val) {
alert('setting: ' + val);
propertyDescriptorSrc.set.call(this, val);
}
});
</script>
</head>
<body>
<iframe src="page.html"></iframe>
</body>
</html>
I expected the iframe element in the body to load redirect.html instead of page.html, since I overrided its "getter", but it still loaded page.html.
Is there a way to force this behavior where all iframes by default go to redirect.html instead of whatever is defined in their src attribute?
(This is just an experimental project)
Before it starts javascript, the DOM tree is already parsed and contains all iframes together with its src, according to the Critical Rendering Path.
The only way to do this is by using javascript to redefine the src attributes of the individual iframe node. For example, as below.
all iframes are set to redirect.html:
<!doctype html>
<html>
<head>
</head>
<body>
<iframe src="page.html"></iframe>
<iframe src="page2.html"></iframe>
<script>
( function(){
var lng = document.getElementsByTagName('iframe').length;
for (var i=0; i<lng;i++){
document.getElementsByTagName('iframe')[i].src="redirect.html";
}
})();
</script>
</body>
</html>
According to the suggestion, #Aaron Digulla gives a more readable form of function.
It seems that the search algorithms of the DOM tree are so efficient today that the argument is the readability of the record, not the efficiency.
(function(){
var frames = document.getElementsByTagName('iframe');
for (var i=0; i<frames.length;i++){
frames[i].src="redirect.html";
}
})();

How to capture console.log from external sources?

I understand how to modify the console.log function to intercept the logs and push it into an array. However this does not seem to work for all console log. In my html, I've loaded a html webapp (\creative\300x600 with video\index.html) that also does a console.log("click detected") that I can see in my chrome dev tools, but is not captured by console. ogs (The second script in the code example). I suspect that because this is an external file being loaded in, I cannot intercept it.
Is there a solution to get all console.log from any source and save it?
<!DOCTYPE html>
<head>
<script>
//add index.html to div on click
function load_home() {
console.log("Loaded");
document.getElementById("content").src='creative\\300x600 with video\\index.html';
}
window.addEventListener("click",load_home);
</script>
<script>
//modify console.log
console.defaultLog = console.log.bind(console);
console.logs = [];
console.log = function() {
console.defaultLog.apply(console, arguments);
console.logs.push(Array.from(arguments));
};
</script>
</head>
<html>
<iframe id="content" style="width: 300px; height:600px; background-color:blue" ></iframe>
</body>
</html>
EDIT:
Tried making it on a div rather than in an iframe, same issue of not being able to record the console.log of the external html. Only console.log("loaded") from the load_home function gets logged.
<!DOCTYPE html>
<head>
<script>
function load_home() {
console.log("Loaded");
document.getElementById("content").innerHTML='<object type="text/html" data="creative\\300x600 with video\\index.html" ></object>';
}
window.addEventListener("click",load_home);
</script>
<script>
console.defaultLog = console.log.bind(console);
console.logs = [];
console.log = function() {
console.defaultLog.apply(console, arguments);
console.logs.push(Array.from(arguments));
};
</script>
</head>
<html>
<div id="content" style="width: 300px; height:600px; z-index:-1;" ,"onclick=load_home();" </div>
</body>
</html>
The problem here is because of that iframe. iframes have a different window object, independent from the parent's window object. This means whatever modifications you did to the parent's window.console will not affect the iframe's window.console.
You could get the frame's window object via contentWindow and modify its console. But you'd have to do this for each present and future iframe. Additionally, you cannot access the contentWindow of a iframe rendering a page from another domain.
If you're attempting to capture the logs of all the frames and somehow consolidate them into one big log, a better option is to have that same console-altering script on every page you want tracked. Then send all your logs to the server, ordered by timestamp or something. That's pretty much how Sentry works.

How can I prevent JavaScript in an iFrame to access properties of the outer site, even if the iFrame's content comes from the same origin?

Basically I want to have an iFrame which always restricts it's content as if it comes from a different domain, even if the content comes from the same origin.
Is there any way to do this?
The best solution is probably to use the HTML5 sandbox attribute on the iframe, which (by default) explicitly disables both scripting and same-origin access to the parent DOM.
Good introduction at http://msdn.microsoft.com/en-us/hh563496.aspx
As of Dec 2012, this seems to be supported on most current browsers.
This will hide window.parent in the child frame/window, but not the top property.
BUT the window.parent property is STILL accessible till the end of the onload event of the child window/frame.
<html>
<head>
<style type="text/css">
#wrapper {width:1000px;height:600px;}
</style>
<script type="text/javascript">
window.onload = function() {
var frm = document.getElementById('childFrame');
var win = frm.contentWindow || (frm.contentDocument && frm.contentDocument.parentWindow) || (frm.document && frm.document.parentWindow);
if (win) win.parent = null;
}
</script>
</head>
<body>
<div id="wrapper">
<iframe id="childFrame" src="child.html" frameborder="0" style="width:100%;height:100%;"></iframe>
</div>
</body>
</html>

Execute Function when iFrame url Changes

I have a signup form which is inside an iframe on my site and I want to create a redirect when the url of the iframe changes (when user successfully signed-up).
These 2 sites are cross domain and I know that it is almost impossible to pull the url cross domains but is there a workaround? I know the src will not change and I was thinking to use onload(), when the iframe loads a second time (when user successfully signed-up), execute a function to redirect to a thank you page.
Here is an example using the javascript 'Porthole'.
Its possible, but keep in mind the safety issues with iframes. The solution: if you have control of the original page + iframe, you can 'trick' the browser by implementing some javascripts on both sides.
First create a 'proxy' page on both domains. Name it 'proxy.html' or something (note: you have to use 'porthole.min.js', you can get this from the sources in the bottom)
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<!-- Replace the url with your own location -->
<script type="text/javascript" src="porthole.min.js"></script>
<script type="text/javascript">
window.onload=function(){ Porthole.WindowProxyDispatcher.start(); };
</script>
</head>
<body>
</body>
</html>
On the parent page: (refer to the iframe proxy.html page)
<script type="text/javascript" src="porthole.min.js"></script>
<iframe id="guestFrame" name="guestFrame" src="http://iframe.otherdomain.com/"></iframe>
<script type="text/javascript">
var iframeDomain = 'http://iframe.otherdomain.com';
var redirectUrl = 'http://www.mydomain.com/redirect-to/signed-up';
function onMessage(messageEvent) {
if (messageEvent.origin == iframeDomain) {
if (messageEvent.data["action"]
&& messageEvent.data["action"] == 'signed-up) {
window.location.href = redirectUrl; // The final action!
// This is the eventual redirect that will happen
// once your visitor has signed-up within the iframe
}
}
}
var windowProxy;
window.onload=function(){
// Create a proxy window to send to and receive messages from the iFrame
windowProxy = new Porthole.WindowProxy(
'http://iframe.otherdomain.com/proxy.html', 'guestFrame');
// Register an event handler to receive messages;
windowProxy.addEventListener(onMessage);
};
</script>
On the iframe page (refer to the parent proxy.html page)
<script type="text/javascript">
var windowProxy;
window.onload=function(){
// Create a proxy window to send to and receive messages from the parent
windowProxy = new Porthole.WindowProxy(
'http://www.mydomain.com/proxy.html');
// Register an event handler to receive messages;
windowProxy.addEventListener(function(event) {
// handle event (not used here, the iframe does not need to listen)
});
};
</script>
From the iframe you can send a message with javascript to the parent page (and also the other way). If you use only 1 javascript within the iframe domain, you can do something like this to send a message to the parent frame when the url is changed to something specific, for example 'signed-up.php' (untested, but you'll get the idea)
<script type="text/javascript">
window.onload=function(){
if(window.location.href.indexOf("signed-up.php") > -1) {
windowProxy.post({'action': 'signed-up'}); // Send message to the parent frame
// On the parent page, the function 'onMessage' is triggered.
}
};
</script>
Sources:
http://ternarylabs.github.io/porthole/
Github: https://github.com/ternarylabs/porthole
Demo: http://sandbox.ternarylabs.com/porthole/

Cross domain iframe src changes

I have an application which contains an iframe. I can modify the contents of the iframe, but not the whole page itself, e.g.:
<html><head></head>
<body>
<iframe>
<!-- my code -->
</iframe>
</body></html>
I have a requirement in which I need to change contents of the iframe to a different page (possibly on a different domain) and go back. Currently I do it like this:
The first page (it is inside the iframe executionPanelApplications):
<html>
<head>
<script type="text/javascript">
function replaceIFrameUrl() {
var doSubmit = "<c:out value='${param.doSubmit}'/>";
if (doSubmit == 1) {
document.forms['testForm'].submit();
}
else {
var adfUrl = "<fuego:fieldValue att='instJs.adfUrl' onlyValue='true'/>";
var bpmSrc = parent.document.getElementById('executionPanelApplications').src;
var bpmSrcParams = bpmSrc.split('&');
var activityId = (bpmSrcParams[1].split('='))[1];
var url = adfUrl +"&actionType=0&activityId="+activityId;
parent.document.getElementById('executionPanelApplications').src = url;
}
};
</script>
</head>
<body onload="replaceIFrameUrl();">
<form method="post" id="testForm" name="testForm" />
</body>
</html>
The second page (it should be inside the iframe executionPanelApplications as well):
<script>
function leave(e) {
var iframe = parent.parent.document.getElementById("executionPanelApplications");
iframe.src = url;
};
</script>
If both sites are in localhost it works like a charm. Unfortunatelly if they are in different domains - the second page is opened in a new window. Tested in ie 8. As i said - i can't change the contents of the page that contains the iframe. I can only work from inside the iframe. I need this to work only in ie.
Any ideas?
I would not use iframes, because there are different security restrictions (cross domain communication)
But I think, this one can help you: http://softwareas.com/cross-domain-communication-with-iframes

Categories

Resources