I'd like to run some javascript in the context of an iframe's window. Right now the only way I can think to do that is to inject a script tag:
myIframe = document.createElement('iframe');
myIframe.setAttribute('name', 'xyz123');
document.body.appendChild(myIframe);
myIframe.contentWindow.document.write(`
<script>
console.log('The current window name is:', window.name);
</script>
`);
Note: this is a same-domain iframe, without a src, so I have full access to the contentWindow.
It's important for my use case that the code runs with the correct globals; window, document etc should all be scoped to the iframe itself.
Is there any other way I can do this? The above works, but the script needs to run on different domains all with different CSP rules, which means adding support for nonces/hashes etc.
Is it possible to do something like:
myIframe.contentWindow.run(function() {
console.log('The current window name is:' window.name);
});
I've tried myIframe.contentWindow.setTimeout but that still seems to run the code in the context of the parent window.
You can actually create that run function, and then apply a callback function to this which of course will be the iframe context. Then you can access iframe elements by using this:
myIframe.contentWindow.run = function(fn) {
fn.apply(this);
};
myIframe.contentWindow.run(function() {
console.log('(run) The current window name is:', this.window.name);
});
Console output
(run) The current window name is: xyz123
You can check my example here: http://zikro.gr/dbg/html/con-frame/
EDIT
If you want to just use window rather than this.window, then you can create a parameter to the inline function with he name window, and then just pass this.window to that function like this:
myIframe.contentWindow.run = function(fn) {
fn.call(this, this.window);
};
myIframe.contentWindow.run(function(window) {
console.log('(run) The current window name is:', window.name);
});
And it still works as expected.
Maybe split the javascript to part run from main window (let's call it main.js) and from iframe (let's call it iframe.js). Then in iframe's src place link to iframe.js or iframe.html which loads js file (I'm not sure if you can include javascript straight from src attribute).
If you load js into the iframe, use solution at Calling a function inside an iframe from outside the iframe.
window.name='main window'; // just for debugging
var myIframe = document.createElement('iframe'), frameScript = document.createElement('script');
document.body.appendChild(myIframe);
frameScript.textContent = "window.top.name='topWindow';window.name = 'xyz123';function WhereAmI(){return(window.name);} window.parent.postMessage('frame_initialized', '*'); "
myIframe.contentWindow.document.documentElement.appendChild(frameScript);
function OnMessage(event) {
if(typeof event.data == 'string') switch(event.data) {
case 'frame_initialized':
myIframe.contentWindow.document.body.appendChild( document.createTextNode(myIframe.contentWindow.WhereAmI()) );
console.log('Now running in', myIframe.contentWindow.WhereAmI());
break;
}
}
window.addEventListener('message', OnMessage, false);
Tested with Firefox and Chromium.
Instead of .textContent you can apply the .src to frameScript, so the script can load asynchronously. Then you can call postMessage as shown above or call a callback function to notify the parent window.
Note that in your original code window.frameElement.name is initialized. Your script asks then for window.name.
FireFox copies the value automatically to the window, causing some confusion.
You need to load the script asynchronously (i.e. $.getScript()) and then invoke it on .contentWindow. I haven't tested but this should work.
(function() {
var myIframe = document.createElement('iframe#iframe'):
var jsSnippet;
var iframeScript = function(url) {
script = document.createElement('script'),
scripts = document.getElementsByTagName('script')[0];
script.src = url;
return jsSnippet = scripts.parentNode.insertBefore(script, scripts);
};
myIframe.setAttribute('name', 'xyz123'):
myIframe.appendChild(jsSnippet);
document.body.appendChild(myIframe);
return document.getElementById('#iframe').contentWindow.iframeScript();
})
These two resources will be helpful if this solution doesn't work:
https://plainjs.com/javascript/ajax/load-a-script-file-asynchronously-49/
Invoking JavaScript code in an iframe from the parent page
Related
Can I create a Google chrome extension to prevent the page from doing an alert() ?
As #MrGlass said, currently, Chrome Extensions run in a separate environment, limiting access to the actual window object and providing a duplicate that is only valid for the extension.
To solve this, we can inject a script element directly into the document. This way, you access the document's environment and the real window object.
First, lets create the function (I added the "confirm" as well, because some confirms were annoying me so much):
var disablerFunction = function () {
window.alert = function alert(msg) { console.log('Hidden Alert ' + msg); };
window.confirm = function confirm(msg) {
console.log("Hidden Confirm " + msg);
return true; /*simulates user clicking yes*/
};
};
Now, what we're going to do is to transform that function in a text script and enclose it in parentheses (to avoid possible conflicts with actual vars in the page environment):
var disablerCode = "(" + disablerFunction.toString() + ")();";
And finally, we inject a script element, and immediately remove it:
var disablerScriptElement = document.createElement('script');
disablerScriptElement.textContent = disablerCode;
document.documentElement.appendChild(disablerScriptElement);
disablerScriptElement.parentNode.removeChild(disablerScriptElement);
Yes you can, alert() is just a JavaScript method, you can override its functionality by doing.
window.alert = function alert(msg) {
console.log('Hidden Alert ' + msg);
};
Just remember to run that content script at document_start within the manifest via run_at manifest content script modifier.
I believe there is an extension that just does that. The developer names it Nice Alert.
https://chrome.google.com/extensions/detail/ehnbelnegmgdnjaghgomaakjcmpcakhk
Thank you. That helped. However, I realized I needed to do this to get it to work
location.href="javascript: window.alert = function(x) {console.log(x)};"
if I wanted to remove alerts and confirms, I can do
location.href="javascript: window.alert = function(x) {console.log(x)}; window.confirm = function(){return true;};";
I couldn't find how to do this after digging through S\O. Im using javascript to create an iframe and then i'm loading jQuery into the Iframe. Now I just want to call methods such as $ajax in the Iframe context.
Below is how i did it.
var iframe = document.createElement('iframe');
iframe.name = "loginFrame";
iframe.id = "loginFrame";
document.body.appendChild(iframe);
var idocument = iframe.contentWindow.document;
var jq = idocument.createElement('script');
jq.src = "https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js";
idocument.getElementsByTagName('body')[0].appendChild(jq);
jq.onload = function () {
}
This works and idocument will in fact have the jQuery script in its body. Now using the Iframe, how would I make calls using jQuery?
idocument.$.ajax(url, settings)
Does not work and returns
Uncaught TypeError: Cannot read property 'ajax' of undefined error. Any help is much appreciated?
EDIT: For the following method to work properly the iframe src cannot point to a different domain, and must be set, for example
iframe.src = "about:blank"
The jquery "$" is an attribute of the window object of the iframe.
So you can access jquery using
iframe.contentWindow.window.$
If you wanted to make an ajax call you would do:
iframe.contentWindow.window.$.ajax(url, settings)
Also if you want to use Jquery the normal way you can do
$ = iframe.contentWindow.window.jQuery
Note: This should be in jq.onload and this will conflict with jQuery if it is on the main page so you could instead do:
iframe.$ = iframe.contentWindow.window.jQuery
Hope this helps!
Have you tried this? iframe.contentWindow.$.ajax(url, settings)
I found this worked for me using a method from another SO solution:
[Stackoverflow] Why does appending a <script> to a dynamically created <iframe> seem to run the script in the parent page?
First 3 lines of your code then:
//* Updated to use the 2.1.4 jquery...(and tested still works!)
// create a string to use as a new document object
var val = '<scr' + 'ipt type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></scr' + 'ipt>';
//If you want to add further script tags un-comment:
//val += '<scr' + 'ipt type="text/javascript"> $(function() { $("body").append("<h1>It works!</h1>"); }); </scr' + 'ipt>';
// get a handle on the <iframe>d document (in a cross-browser way)
var doc = iframe.contentWindow || iframe.contentDocument;
if (doc.document) {
doc = doc.document;
}
// open, write content to, and close the document
doc.open();
doc.write(val);
doc.close();
//Assuming this is the first script tag in the iframe...
doc.getElementsByTagName('script')[0].onload = function () {
//* necessary for cross-browser function have to get the original document context:
var doc = iframe.contentWindow || iframe.contentDocument;
//Edited to initialize $ not in global namespace yet:
// to scope the jQuery's $ to the iframe...
var $ = doc.$;
//Finally you can use jQuery to do ajax
$.ajax({ });
}
Out of curiosity why can you not give the parent window access to jQuery?
Something to do with security issues?
This question asks for a way to open a new window using window.open and then inject it with a script. It was not possible because of cross-domain security issues.
However, my problem is that I want to do the exact same thing, except from the same domain to the same domain. Is this possible?
Note that .write does not solve this problem because it wipes all the html from the page first.
You can do something like this:
var theWindow = window.open('http://stackoverflow.com'),
theDoc = theWindow.document,
theScript = document.createElement('script');
function injectThis() {
// The code you want to inject goes here
alert(document.body.innerHTML);
}
theScript.innerHTML = 'window.onload = ' + injectThis.toString() + ';';
theDoc.body.appendChild(theScript);
This also seems to work:
var theWindow = window.open('http://stackoverflow.com'),
theScript = document.createElement('script');
function injectThis() {
// The code you want to inject goes here
alert(document.body.innerHTML);
}
// Self executing function
theScript.innerHTML = '(' + injectThis.toString() + '());';
theWindow.onload = function () {
// Append the script to the new window's body.
// Only seems to work with `this`
this.document.body.appendChild(theScript);
};
And if for some reason you want to use eval:
var theWindow = window.open('http://stackoverflow.com'),
theScript;
function injectThis() {
// The code you want to inject goes here
alert(document.body.innerHTML);
}
// Self executing function
theScript = '(' + injectThis.toString() + '());';
theWindow.onload = function () {
this.eval(theScript);
};
What this does (Explanation for the first bit of code. All examples are quite similar):
Opens the new window
Gets a reference to the new window's document
Creates a script element
Places all the code you want to 'inject' into a function
Changes the script's innerHTML to load said function when the window
loads, with the window.onload event (you can also use addEventListener). I used toString() for convenience, so you don't have to concatenate a bunch of strings. toString basically returns the whole injectThis function as a string.
Appends the script to the new window's document.body, it won't actually append it to the document that is loaded, it appends it before it loads (to an empty body), and that's why you have to use window.onload, so that your script can manipulate the new document.
It's probably a good idea to use window.addEventListener('load', injectThis.toString()); instead of window.onload, in case you already have a script within your new page that uses the window.onload event (it'd overwrite the injection script).
Note that you can do anything inside of the injectThis function: append DIVs, do DOM queries, add even more scripts, etc...
Also note that you can manipulate the new window's DOM inside of the theWindow.onload event, using this.
Yes...
var w = window.open(<your local url>);
w.document.write('<html><head>...</head><body>...</body></html>');
Here's a trick I use, it uses query strings, and is client side. Not perfect but it works:
On the sending page, do:
var javascriptToSend = encodeURIComponent("alert('Hi!');");
window.open('mypage.html?javascript=' + javascriptToSend);
Replace mypage.html with your page. Now on the receiving page, add:
(location.href.match(/(?:javascript)=([^&]+)/)[1])&&eval(decodeURIComponent(location.href.match(/(?:javascript)=([^&]+)/)[1]));
You'll have to do some back-and forth to make sure this works.
If you HAVE PHP you can use this more reliable solution on the receiving page:
eval(decodeURIComponent(<?=$_GET['javascript'] ?>));
I have one HTML page with parent.js in it. Within that html, one iFrame will be created on runtime. This iframe needs the parent.js in its context.
If I am having one
function save() { ... }
in the parent.js, I can call that from iframe.js, like
parent.save();
But i need to call that in the iframe context, like
save();
So I have loaded the parent.js again in the iframe html. This makes parent.js to be loaded everytime I create a new iframe.
Is there anyway I could reuse the parent.js which is already loaded, into each iframe created?, like
loadParentJS("parent.js");
within iframe.js. This shouldn't give another request to application server.
Encapsulate your code in parent.js in a closure:
var loadParentJS = function(window) {
window.save = function() {
// code
};
window.other = function() {
// code
};
// rest of your code...
};
loadParentJS(window);
Then in your iframe, run this:
parent.loadParentJS(window);
In your parent.js file, preceed each function and variable with this. and then you should be able to take advantage of Javascript Closures.
Replace: function save() { ... }
With: this.save = function() { ... }
Replace: var aVariable = "value";
With: this.aVariable = "value";
Then in your iframe you need to set the scope of this to parent:
this = parent;
All of your calls to functions or variables in parent.js (in the global javascript or in the iframe javascript) will look like this:
this.save();
alert(this.aVariable);
Short answer, no you can't. the content of IFrame is handled as a separate web page.
Longer answer: are you sure you need to use IFrame? what is it for? can you avoid it?
using IFrame is not a bad thing, just maybe it doesn't fullfill your needs
You should read a bit about cross domain communication. This will give you an idea on how to communicate between your web page and the IFrame inside it
cross domain communication
good luck
I guess you can pass the document or window to the save function to manipulate the context.
For example, accept the document as a parameter:
function save(doc) { doc.getObjectById("myform").submit(); }
Then:
parent.save(document);
Or accept the window as the parameter:
function save(win) { alert(win.myvariable) }
And then:
parent.save(self);
I have a WebBrowser control in my C# application. The web browser is under the user's control, that is, he can load any web page his computer can access on the web (of course limited by proxy, hosts file and so on).
I need to know and to be notified when there is a Javascript call inside the page loaded in the web browser component.
First example: given a link like this
test
When the user clicks the link I need to know that the function "jsFunct" has been called.
Second example: given a call like
<script type="text/javascript">
window.setTimeout("jsFunct()", 1000);
</script>
I need to know that, 1 second after the execution of the script, the function jsFunct has been called.
The best thing would be to have an event fired when the function is called. It would also be great if the event could get the Javascript code executed, or at least the function name in the arguments.
EDIT:
Even if the question is related to the webbrowser component, anything that allows the user to detect javascript activation (even via js) would be fine, being able to inject a js that handles the javascript event and passes it to the wb control triggering some event that it can handle.
You can use window.external to call a C# method when a global function is fired in JavaScript. See WebBrowser Control Overview for details on window.external.
You'll need to set ObjectForScripting: Webbrowser control's window.external is ALWAYS null. for this to work.
Take #Krishna's answer to add the JavaScript (but drop jQuery because it won't be needed):
private void addScript(HtmlElement head, string scriptSource)
{
HtmlElement lhe_script = head.Document.CreateElement("script");
IHTMLScriptElement script = (IHTMLScriptElement)lhe_script.DomElement;
script.src = scriptSource;
head.AppendChild(lhe_script);
}
addScript(WebBrowser.Head, #"InjectMonitor.js");
The JavaScript below (InjectMonitor.js) will find all global functions and attach your specified handler:
function augment(withFn) {
var name, fn;
for (name in window) {
fn = window[name];
if (typeof fn === 'function') {
window[name] = (function(name, fn) {
var args = arguments;
return function() {
withFn.apply(this, args);
fn.apply(this, arguments);
};
})(name, fn);
}
}
}
augment(function(name, fn) {
console.log("calling " + name, fn);
// window.external.yourC#method
});
In this example, taken from Adding Console Log to Every Function, it just logs the call to console; but using window.external you could send some message back to your C# application with details of what function was called from the client.
Finally, here's a JS Bin example (run it and don't forget the console): JS Bin Example
On the webbrowser load event,
Inject Jquery
Inject Monitor scripts
,
private void addScript(HtmlElement head, string scriptSource)
{
HtmlElement lhe_script = head.Document.CreateElement("script");
IHTMLScriptElement script = (IHTMLScriptElement)lhe_script.DomElement;
script.src = scriptSource;
head.AppendChild(lhe_script);
}
addScript(Webbrowser.Head, #"<Change File Path here>jquery.min.js");
addScript(WebBrowser.Head, #"InjectMonitor.js");
your file InjectMonitor.js should be something like this
$(document).ready(function () {
//Add click event for every anchor on the page loaded- note this merely alerts text on click. you can however add your own function
$("a").click(function (e) { alert($(this).text()); return false;})
});
Well what krishna has answered is interms of pure javascript attaching to events, however i see that you might need to attach it to all the tags(a,p,div,input) etc and to all the events attached to each tag.
i believe the another way is to play around with the BHO(browser helper object) available to your in .net, and if not and you are good at VC++ and MFC you can also play around with Windows Hooks.