Run Javascript in iFrame context - javascript

I am attempting to run a script in a webpage, that should be executed in an <iframe>. Right now I can call a function that is set within the <iframe>.. I'm just having issues running a script to the <iframe>'s context.
Here's how I run a function set in the <iframe>
$('#iframe').get(0).contentWindow.performSearch();
Now instead of calling the preformSearch function, I wish to run a script - for this example, this is the script...
console.log('worked!');
My actual script is a big one, so I won't put it here - for the sake of this question.
So, is there any way to run that script through the <iframe>'s context? For example, my first guess would be/was..
$('#iframe').get(0).contentWindow.function(){
console.log('worked!');
}
I've never messed with running functions through something like an <iframe> before though, so I'm stumped.
Thanks for any help in advance!
NOTE I am using NW.js (Node-Webkit) to remove all <iframe> restrictions.
NOTE v2 The preformSearch() function was just a reforence on how I call functions in the frame.

You could try and use the messaging mechanizm
which means that on the parent frame you could send the message with
var win =$("#iframe").contentWindow;
win.postMessage({ messageType: "predefinedMessage", features: data }, '*');
and than in the iframe you could get the message
function FrameCommunicator(attachManager) {
var mfilesFrame;
function _actOnMessage(data) {
if (data.messageType === "predefinedMessage") {
//perform here what you need
}
}
window.addEventListener("message", function (e) {
var data = e.data;
mfilesFrame = e.source;
_actOnMessage(data);
}, false);
}
now the thing is that the iframe and the parent fram can have the same code if they reference to the remotely.
Another way would be just to send the content of the message as JS and run it with eval , but that is risky , bad practice and lotsa other things :)

Related

Unable to access Global variables in chrome.tabs.executescript in Chrome version 55

I recently updated Chrome to version 55.0.2883.75.
I am using a self developed Chrome Plugin to parse my HTML files wherein I use chrome.tabs.executescript to get data from background HTML page.
So when I execute chrome.extension.onRequest, I save the background page's parsed data to a global variable and access it in the callback function of chrome.tabs.executescript and process it.
This was working fine till I update to Version 55.0.2883.75.
How can I access the global variables in the new version ??
My Code Below :
Step 1 :
chrome.extension.onRequest.addListener(
function (request, sender, sendResponse) {
parser = new DOMParser();
htmlDoc = parser.parseFromString(request.content, "text/html");
//outputJson is a global variable which is Populated here
outputJson = parseMyPage(outputJson, htmlDoc);
});
Step 2:
chrome.tabs.getSelected(null, function (tab) {
// Now inject a script onto the page
chrome.tabs.executeScript(tab.id,{
code: "chrome.extension.sendRequest({content: document.body.innerHTML}, function(response) { console.log('success'); });"
}, function () {
//my code to access global variables
if (outputJson && null != outputJson) {
// other stuff
}
});
});
The way your code is designed, you are relying on the order in which two asynchronous blocks of code are executed: the extension.onRequest1 event and the callback for tabs.executeScript(). Your code requires that the extension.onRequest1 event fires before the tabs.executeScript() callback is executed. There is no guarantee that this will be the order in which these occur. If this is a released extension, it is quite possible that this was failing on users' machines, depending on their configuration. It is also possible that the code in Chrome, prior to Chrome 55, resulted in the event and callback always happening in the order you required.
The solution is to to rewrite this to not require any particular order for the execution of these asynchronous code blocks. Fortunately, there is a way to do that and reduce complexity at the same time.
You can transfer the information you desire from the content script to your background script directly into the callback of the tabs.executeScript(), without the need to explicitly pass a message. The value of the executed script is passed to the callback in an array containing one entry per frame in which the script was injected. This can very conveniently be used to pass data from a content script to the tabs.executeScript() callback. Obviously, you can only send back a single value per frame this way.
The following code should do what you desire. I hand edited this code from your code in this Question and my answer here. While the code in that answer is fully tested, the fact that I edited this only within this answer means that some errors may have crept in:
chrome.tabs.getSelected(null, function (tab) {
// Now inject a script onto the page
chrome.tabs.executeScript(tab.id,{
code: "document.body.innerHTML;"
}, function (results) {
parser = new DOMParser();
htmlDoc = parser.parseFromString(results[0], "text/html");
//outputJson is a global variable which is Populated here
outputJson = parseMyPage(outputJson, htmlDoc);
//my code to access global variables
if (outputJson && null != outputJson) {
// other stuff
}
});
});
extension.sendRequest() and extension.onRequest have been deprecated since Chrome 33. You should replace these anywhere you are using them with runtime.sendmessage() and runtime.onMessage.

(cross-domain) Call parent function from outside iframe is forbidden

...but I do control part of the javascript inside the second domain (which integrates the iframe).
So, what I need is some workaround my problem. We have example2.com (this one holds the iframe) and example.com (this one is the original, within the iframe). Inside the iframe the user clicks a button that executes parent.redirectUser() and although I have that function defined in example2.com it fails to execute because it points the function as forbidden to access from within the iframe. Considering I can control the javascript in example2.com, is there any other way to workaround this situation? Thank you very much for your help...
Yes, you can do it.
You can use messages technique,
send message from child(example2.com):
parent.postMessage("Ok", "example.com");
in parent(example.com) you must add the following code:
//add eventlistener for message event
if (window.addEventListener) {
window.addEventListener("message", listener);
} else {
// IE8
window.attachEvent("onmessage", listener);
}
function listener(event) {
//check the message trust or not?
if (event.data == "Ok") {
//from parent you can call function, but function must be placed in global scope
redirectUser();
}
}

Chrome extensions: best method for communicating between background page and a web site page script

What I want to do is to run go() function in image.js file. I've googled around and I understand that is not possible to run inline scripts.
What is the best method to call the JavaScript I want? Events? Messages? Requests? Any other way?
Here is my code so far:
background.js
chrome.browserAction.onClicked.addListener(function(tab) {
var viewTabUrl = chrome.extension.getURL('image.html');
var newURL = "image.html";
chrome.tabs.create({
url : newURL
});
var tabs = chrome.tabs.query({}, function(tabs) {
for (var i = 0; i < tabs.length; i++) {
var tab = tabs[i];
if (tab.url == viewTabUrl) {
//here i want to call go() function from image.js
}
}
});
});
image.html
<html>
<body>
<script src="js/image.js"></script>
</body>
</html>
image.js
function go(){
alert('working!');
}
There are various ways to achieve this. Based on what exactly you are trying to achieve (which is not clear by your question), one way might be better than the other.
An easy way, would be to inject a content script and communicate with it through Message Passing, but it is not possible to inject content scripts into a page with the chrome-extension:// scheme (despite what the docs say - there is an open issue for correcting the docs).
So, here is one possibility: Use window.postMessage
E.g.:
In background.js:
var viewTabURL = chrome.extension.getURL("image.html");
var win = window.open(viewTabURL); // <-- you need to open the tab like this
// in order to be able to use `postMessage()`
function requestToInvokeGo() {
win.postMessage("Go", viewTabURL);
}
image.js:
window.addEventListener("message", function(evt) {
if (location.href.indexOf(evt.origin) !== -1) {
/* OK, I know this guy */
if (evt.data === "Go") {
/* Master says: "Go" */
alert("Went !");
}
}
});
In general, the easiest method to communicate between the background page and extension views is via direct access to the respective window objects. That way you can invoke functions or access defined properties in the other page.
Obtaining the window object of the background page from another extension page is straightforward: use chrome.extension.getBackgroundPage(), or chrome.runtime.getBackgroundPage(callback) if it's an event page.
To obtain the window object of an extension page from the background page you have at least three options:
Loop through the results of chrome.extension.getViews({type:'tab'}) to find the page you want.
Open the page in the first place using window.open, which directly returns the window object.
Make code in the extension page call a function in the background page to register itself, passing its window object as a parameter. See for instance this answer.
Once you have a reference to the window object of your page, you can call its functions directly: win.go()
As a side note, in your case you are opening an extension view, and then immediately want to invoke a function in it without passing any information from the background page. The easiest way to achieve that would be to simply make the view run the function when it loads. You just need to add the following line to the end of your image.js script:
go();
Note also that the code in your example will probably fail to find your tab, because chrome.tabs.create is asynchronous and will return before your tab is created.

Using script tag to pass arguments to JavaScript

I need to implement a cross-site comet http server push mechanism using script tag long polling. (phew...) For this, I dynamically insert script tags into the DOM and the server sends back short js scripts that simply call a local callback function that processes the incoming messages. I am trying to figure out a way to associate each one of these callback calls with the script tag that sent it, to match incoming replies with their corresponding requests.
Clearly, I could simply include a request ID in the GET url, which is then returned back in the js script that the server generates, but this creates a bunch of unnecessary traffic and doesn't strike me as particularly elegant or clever.
What I would like to do is to somehow associate the request ID with the script tag that I generate and then read out this request ID from within the callback function that is called from inside this script tag. That way, all the request management would remain on the client.
This leads me to the following question: Is there a way to ask the browser for the DOM element of the currently executing script tag, so I can use the tag element to pass arguments to the contained javascript?
I found this thread:
Getting the currently executing, dynamically appended, script tag
Which is asking exactly this question, but the accepted answer isn't useful to me since it still requires bloat in the server-returned js script (setting marker-variables inside the script) and it relies on unique filenames for the scripts, which I don't have.
Also, this thread is related:
How may I reference the script tag that loaded the currently-executing script?
And, among other things, suggests to simply grab the last script in the DOM, as they are executed in order. But this seems to only work while the page is loading and not in a scenario where scripts are added dynamically and may complete loading in an order that is independent of their insertion.
Any thoughts?
PS: I am looking for a client-only solution, i.e. no request IDs or unique callback function names or other non-payload data that needs to get sent to and handled by the server. I would like for the server to (theoretically) be able to return two 100% identical scripts and the client still being able to associate them correctly.
I know you would like to avoid discussions about changing the approach, but that's really what you need to do.
First, each of the script tags being added to the DOM to fire off the poll request is disposable, i.e. each needs to be removed from the DOM as soon as its purpose has been served. Else you end up flooding your client DOM with hundreds or more dead script tags.
A good comparable example of how this works is jsonp implementations. You create a client-side named function, create your script tag to make the remote request, and pass the function name in the request. The response script wraps the json object in a function call with the name, which then executes the function on return and passes the json payload into your function. After execution, the client-side function is then deleted. jQuery does this by creating randomly generated names (they exist in the global context, which is really the only way this process works), and then deletes the callback function when its done.
In regards to long polling, its a very similar process. Inherently, there is no need for the response function call to know, nor care, about what script tag initiated it.
Lets look at an example script:
window.callback = function(obj){
console.log(obj);
}
setInterval(function(){
var remote = document.createElement('script');
remote.src = 'http://jsonip.com/callback';
remote.addEventListener('load', function(){
remote.parentNode.removeChild(remote);
},false);
document.querySelector('head').appendChild(remote);
}, 2000);​
This script keeps no references to the script elements because again, they are disposable. As soon as their jobs are done, they are summarily shot.
The example can be slightly modified to not use a setInterval, in which case you would replace setInterval with a named function and add logic into the remote load event to trigger the function when the load event completes. That way, the timing between script tag events depends on the response time of your server and is much closer to the actual long polling process.
You can extend this even further by using a queueing system to manage your callbacks. This could be useful if you have different functions to respond to different kinds of data coming back.
Alternatively, and probably better, is to have login in your callback function that handles the data returned from each poll and executes whatever other specific client-side logic at that point. This also means you only need 1 callback function and can get away from creating randomly generated callback names.
If you need more assistance with this, leave a comment with any specific questions and I can go into more detail.
It's most definitely possible but you need a little trick. It's a common technique known as JSONP.
In JavaScript:
var get_a_unique_name = (function () {
var counter = 0;
return function () {
counter += 1;
return "function_" + counter;
}
}()); // no magic, just a closure
var script = document.createElement("script");
var callback_name = get_a_unique_name();
script.src = "/request.php?id=12345&callback_name=" + callback_name;
// register the callback function globally
window[callback_name] = function (the_data) {
console.log(the_data);
handle_data(the_data); // implement this function
};
// add the script
document.head.appendChild(script);
The serverside you can have:
$callback_name = $_GET["callback_name"];
$the_data = handle_request($_GET["id"]); // implement handle_request
echo $callback_name . "(" . json_encode($the_data) . ");";
exit; // done
The script that is returened by /request.php?id=12345&callback_name=XXX will look something like this:
function_0({ "hello": "world", "foo" : "bar" });
There may be a solution using onload/onreadystate events on the script. I can pass these events a closure function that carries my request ID. Then, the callback function doesn't handle the server reply immediately but instead stores it in a global variable. The onload/onreadystate handler then picks up the last stored reply and tags it with the request ID it knows and then processes the reply.
For this to work, I need to be able to rely on the order of events. If onload is always executed right after the corresponding script tag finishes execution, this will work beautifully. But, if I have two tags loading simultaneously and they return at the same time and there is a chance that the browser will execute both and afterwards execute botth onload/onreadystate events, then I will loose one reply this way.
Does anyone have any insight on this?
.
Here's some code to demonstrate this:
function loadScript(url, requestID) {
var script = document.createElement('script');
script.setAttribute("src", url);
script.setAttribute("type", "text/javascript");
script.setAttribute("language", "javascript");
script.onerror = script.onload = function() {
script.onerror = script.onload = script.onreadystatechange = function () {}
document.body.removeChild(script);
completeRequest(requestID);
}
script.onreadystatechange = function () {
if (script.readyState == 'loaded' || script.readyState == 'complete') {
script.onerror = script.onload = script.onreadystatechange = function () {}
document.body.removeChild(script);
completeRequest(requestID);
}
}
document.body.appendChild(script);
}
var lastReply;
function myCallback(reply) {
lastReply = reply;
}
function completeRequest(requestID) {
processReply(requestID, lastReply);
}
function processReply(requestID, reply) {
// Do something
}
Now, the server simply returns scripts of the form
myCallback(message);
and doesn't need to worry at all about request IDs and such and can always use the same callback function.
The question is: If I have two scripts returning "simultaneously" is it possible that this leads to the following calling order:
myCallback(message1);
myCallback(message2);
completeRequest(requestID1);
completeRequest(requestID2);
If so, I would loose the actual reply to request 1 and wrongly associate the reply to request 2 with request 1.
It should be quite simple. There is only one script element for each server "connection", and it can easily be stored in a scoped, static variable.
function connect(nameOfCallback, eventCallback) {
var script;
window[nameOfCallback] = function() { // this is what the response invokes
reload();
eventCallback.call(null, arguments);
};
reload();
function reload() {
if (script && script.parentNode)
script.parentNode.removeChild(script);
script = document.createElement(script);
script.src = "…";
script.type = "text/javascript";
document.head.appendChild(script);
// you might use additional error handling, e.g. something like
// script.onerror = reload;
// but I guess you get the concept
}
}

jQuery.getScript() behaviour

Could someone please explain the behaviour of jQuery's getScript() function?
Consider a javascript file test.js:
var tmp = 'a variable';
alert('here');
When test.js is loaded via html's <script> tag, everything works fine: tmp variable is available in the global scope and a message box appears.
I'm trying to get the similar behavior via this code:
<script>
$(document).ready(function() {
$.getScript("static/js/proto/test.js");
setTimeout(function() {
// at this point tmp should be available
// in the global scope
alert(tmp);
} , 2000); // 2 seconds timeout
}
</script>
But browser's error console reports an "Undefined variable tmp" error.
What am I doing wrong?
Thank you.
$.getScript may be asynchronous, use the callback parameter:
$.getScript("static/js/proto/test.js", function() {
// here you are sure that the script has been executed
});
See the documentation for $.getScript: http://api.jquery.com/jQuery.getScript
The real problem with the script was my lack of experience with JS in general and particulary in AJAX: I was trying to run this script on a local machine without a web server.
Guess what: AJAX expects status '200' from a web server to load a document asynchronously. As there was no web server, the status of the async call was '0'.
Thank you everyone for answering.

Categories

Resources