Read variable and function from injected js script from content-script - javascript

I inject an script from content-script like
content-script.js
var s = document.createElement('script');
// TODO: add "script.js" to web_accessible_resources in manifest.json
s.src = chrome.extension.getURL('latest.js');
s.onload = function(){
console.log(test);
}
(document.body || document.documentElement).appendChild(s);
latest.js
var test = 1000;
It added properly, but now my problem is to read variables and functions from the latest.js on content script because content-script having some data which need to pass in the latest.js via using its functions or variables.
So, onload function I try to console.log(test) which is define in latest.js but I cannot be used it, its undefined.
Help me!!!

According chrome extensions docs about a content scripts:
...content scripts have some limitations. They cannot:
Use variables or functions defined by their extension's pages
Use variables or functions defined by web pages or by other content scripts
So, your latest.js ejects to restricted area and content-script cannot directly read that data.
But the documentation also provides a solution: to communicate via .postMessage.
Example from the docs:
content-script
var port = chrome.runtime.connect();
window.addEventListener("message", function(event) {
// We only accept messages from ourselves
if (event.source != window)
return;
if (event.data.type && (event.data.type == "FROM_PAGE")) {
console.log("Content script received: " + event.data.text);
port.postMessage(event.data.text);
}
}, false);
injected script (latest.js in your case)
document.getElementById("theButton").addEventListener("click",
function() {
window.postMessage({ type: "FROM_PAGE", text: "Hello from the webpage!" }, "*");
}, false);

Related

Error: Permission denied to access property "document"

I have a HTML Document which contains an iframe. Whenever I try to access or modify this iframe with JS I get Error: Permission denied to access property "document".
I am using frame.contentWindow.document.body.innerHTML or frame.contentWindow.document.body.onload or similar such attributes to access or modify the iframe. (In the given code the iframe is referred to as frame.)
For the web-app I am developing, access to these attributes are necessary and I can't do without these (or similar alternatives).
Accessing and then modifying webpages in iframes of other websites is known as Cross-site scripting or XSS and it is a technique used by malicious hackers to prey on unsuspecting victims.
A policy by the name of "Same-Origin Policy" is implemented by browser makers to prevent such behaviour and arbitrary execution of JS code.
This error can be prevented by hosting the parent document and the document in the iframe in the same domain and subdomain, and making sure that the documents are loaded using the same protocol.
Examples of Incompatible Pages:
http://www.example.org & http://www.example2.com
http://abc.example.org & http://xyz.example.com
http://www.example.org & https://www.example.com
Cross-Origin Resource Sharing is a solution to this problem.
For Example:
If http://www.example.com would like to share http://www.example.com/hello with http://www.example.org, a header can be sent with the document which looks like the following:
Access-Control-Allow-Origin: http://www.example.org
To send it with HTML just put it in a <META HTTP-EQUIV="..."> tag, like this:
<head>
...
<META HTTP-EQUIV="Access-Control-Allow-Origin" CONTENT="http://www.example.org">
...
</head>
You can still bypass this issue with the help of YQL even though you don't have access to the header part of the receiving window. With the Postmessage method also you need to edit the recipient window script. But using this method you can load any iframe without touching their scripts. Check this out!
<html>
<iframe src="https://google.com/" width="500" height="300"></iframe>
<script>
var iframe = document.getElementsByTagName('iframe')[0];
var url = iframe.src;
var getData = function (data) {
if (data && data.query && data.query.results && data.query.results.resources && data.query.results.resources.content && data.query.results.resources.status == 200) loadHTML(data.query.results.resources.content);
else if (data && data.error && data.error.description) loadHTML(data.error.description);
else loadHTML('Error: Cannot load ' + url);
};
var loadURL = function (src) {
url = src;
var script = document.createElement('script');
script.src = 'https://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20data.headers%20where%20url%3D%22' + encodeURIComponent(url) + '%22&format=json&diagnostics=true&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys&callback=getData';
document.body.appendChild(script);
};
var loadHTML = function (html) {
iframe.src = 'about:blank';
iframe.contentWindow.document.open();
iframe.contentWindow.document.write(html.replace(/<head>/i, '<head><base href="' + url + '"><scr' + 'ipt>document.addEventListener("click", function(e) { if(e.target && e.target.nodeName == "A") { e.preventDefault(); parent.loadURL(e.target.href); } });</scr' + 'ipt>'));
iframe.contentWindow.document.close();
}
loadURL(iframe.src);
</script>
</html>
You can use postMessage
Window 1 - receiving
window.addEventListener("message", receiveMessage, false);
function receiveMessage(event)
{
var origin = event.origin || event.originalEvent.origin;
// For Chrome, the origin property is in the event.originalEvent object.
if (origin !== "http://example.org:8080")
return;
// ...
}
Window - 2 Transmitting
var popup = window.open(...popup details...);
popup.postMessage(
"The user is 'bob' and the password is 'secret'",
"https://secure.example.net"
);
You have to create another pair to intercommunicate.

How to conditionally include assets in the <head> with Angular

I am building a one-page Angular app. One of the views has content which depends on an external javascript library.
I don't want to include this external JS resource on every view of the site, just the single view which depends on it.
What is the best way to conditionally include blocks of code based on the current view?
If it's possible I'm hoping I can place in the something like this:
<script ng-if="view == 'view1'" type='text/javascript' src='http://webplayer.unity3d.com/download_webplayer-3.x/3.0/uo/UnityObject2.js'></script>
So you should include the library script in page.
Then within directive bound to elements it needs to act on do the initialization.
app.directive('unity', function () {
return {
link: function (scope, element, attrs) {
// element is jQuery object when jQuery.js is included in page
// before angular - or it is a jQLite object similar to a jQuery object
// config code
u.initPlugin(element[0], "web_ovar_beta.unity3d");
}
}
});
Usage in view:
<div unity></div>
This can easily be expanded to pass in attributes to the directive from controller
It is
<script ng-if="view == 'view1'" type='text/javascript' ng-src='http://webplayer.unity3d.com/download_webplayer-3.x/3.0/uo/UnityObject2.js'></script>
And it is something that you don't want to do. It doesn't guarantee that the script won't be loaded twice. Since the script is being used for particular view, make it one-time resolving service
app.factory('unityResolver', function ($document, $rootScope, $q, $timeout) {
var deferred = $q.defer();
var script = angular.element('<script>')[0];
script.src = '...';
script.async = true;
script.onload = script.onreadystatechange = function () {
if (['loaded', 'complete', undefined].indexOf(script.readyState) < 0)
return;
script.onload = script.onreadystatechange = null;
deferred.resolve();
$rootScope.$apply();
}
script.onerror = function onerror() {
script.onerror = null;
deferred.reject();
$rootScope.$apply();
};
$timeout(onerror, 20000);
$document.find('head').append(script);
return deferred.promise;
});
and use it in view/route resolve.
What your wanting to in turn is impossible.
You can get Javascript to include a javascript file if you want however if your using AJAX to load the content that is needed leave it in there it would be the same amount of time to load anyway. but once a file has been loaded into JavaScript it finished with you can remove the file from your HTML but the JavaScript engine still has the content of that file loaded in to it.
Try it in your browsers(chrome or FF with Firebug) inspector now:
open a new tab and go to about:blank then put the code below into the console
var include = (function(){
// the reference to the script
var theScript;
return function (filename, status){
if(status == 'on'){
// adding a script tag
var head = document.getElementsByTagName('head')[0];
theScript= document.createElement('script');
theScript.src = filename;
theScript.type = "text/javascript";
head.appendChild( theScript )
}else{
// removing it again
theScript.parentNode.removeChild( theScript );
}
}
})();
taken from Remove specific <script> tag in <head> tag by onclick event
then in your inspector's console
include("https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js", 'on')
now check Elements and you will see jQuery loaded in the pages head tag.
and type jQueryinto console and you will see function (a,b){return new n.fn.init(a,b)}
And then use this into your console
include("https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js", 'off')
and check your elements tab again the script tag will have gone.
however go back to console and type jQuery again and you will see function (a,b){return new n.fn.init(a,b)} as even though you have unloaded the file you can't remove the executed code from memory.
You have 2 options another framework page gets loaded into an iFrame or if you want to use Anagular to load the View in then just include the file in your head there is no point not having and then removing it as once it is loaded it's loaded

Injecting jQuery to webpage from Safari extension

I just want to inject jQuery into a webpage from a safari extension. But only to some pages because adding jQuery as a start-/endscript would inject it to all pages and this makes browsing slow.
I tried it by creating a script tag using its onload function:
var node = document.createElement('script');
node.onload = function(){
initjquerycheck(function($) {
dosomethingusingjQuery($);
});
};
node.async = "async";
node.type = "text/javascript";
node.src = "https://code.jquery.com/jquery-2.0.3.min.js";
document.getElementsByTagName('head')[0].appendChild(node);
to check if jquery is loaded i use:
initjquerycheck: function(callback) {
if(typeof(jQuery) != 'undefined'){
callback(jQuery);
}else {
window.setTimeout(function() { initjquerycheck(callback); }, 100);
}
}
But typeof(jQuery) remains undefined. (checked that using console.log()).
Only if I call console.log(typeof(jQuery)) from the debugging console it returns 'function'. Any ideas how to fix that? Thanks in advance!
Extension injected scripts cannot access the web page's JavaScript namespace. Your injected script creates a <script> element and adds it to the page's DOM, but then the jQuery object instantiated by the script belongs to the page's namespace, not to your injected script's.
There are at least two potential solutions. One, inject jQuery into the page the normal way, using the extension API. This method is only viable if the web pages that you are targeting can be categorized using URL patterns.
Two, use Window.postMessage to communicate between your injected script and the web page's namespace. You will need to add another <script> to the page, and in this script, have a listener for the message event. The listener will be able to use jQuery as if it were "native" to the page itself.
Here's some code to get you started, if needed.
In the extension injected script:
var s0 = document.createElement('script');
s0.type = 'text/javascript';
s0.src = 'https://code.jquery.com/jquery-2.0.3.min.js';
document.head.appendChild(s0);
var s1 = document.createElement('script');
s1.type = 'text/javascript';
s1.src = safari.extension.baseURI + 'bridge.js';
document.head.appendChild(s1);
window.addEventListener('message', function (e) {
if (e.origin != window.location.origin)
return;
console.log(e.data);
}, false);
window.postMessage('What jQuery version?', window.location.origin);
In bridge.js:
window.addEventListener('message', function (e) {
if (e.origin != window.location.origin)
return;
if (e.data == 'What jQuery version?') {
e.source.postMessage('Version ' + $.fn.jquery, window.location.origin);
}
}, false);

detect and suppress errors loading javascript file

I want to source a javascript file from facebook http://connect.facebook.net/en_US/all.js
The organization I work for has a firewall that blocks access to Facebook, it just goes to an html page that says "Access Denied blah blah blah"
I want to be able to put a javascript src tag <script src="http://... "> </script> and detect and suppress the warnings when the browser tries to evaluate the html as javascript.
Anyone know how?
Looks like jQuery.getScript is what you need as was mentioned. Or you can manually execute:
$.ajax({
url: url,
dataType: 'script',
success: function(){document.write('<script src="http://... "> </script>');}
});
And append your html on successful load with the <script></script> tag.
With the standard <script> tag, not possible. There's nothing really running at the time when the script's src is hit and content downloaded, so you can't wrap that in a try/catch block. There's some tips here on how to dynamically load scripts. Maybe the browsers will add some stuff to the DOM element created there which you can check for.
This is a workaround, not a direct answer, but you could simply set up a reverse proxy outside the firewall for Facebook and load the script from there. Instead of failing more gracefully, it would allow the script not to fail.
Try this, and see if it works for you:
<script type="text/javascript" onerror="throw('An error occurred')" src="http://connect.facebook.net/en_US/all.js"></script>
Alternatively, if you have access to a proxy script to grab external content I would use it via an xmlHttpRequest to grab the JS content. If it is successful, eval the content (yes, eval is evil, I know).
I would add that if you know the JS will fail, then why bother?
Why do you not do this in very simple way?:
if(!window.FB) { // or (typeof window.FB === "undefined")
alert ("ERROR: http://connect.facebook.net/en_US/all.js is not loaded");
}
if(!window.jQuery) { // or (typeof window.jQuery === "undefined")
alert ("ERROR: jQuery is not loaded");
}
// and so on
Please try the function below, it will only call the onload_function if the script has loaded. You can set a timeout to cancel the script.
function include_js(url, onload_function) {
var script = document.createElement("script");
script.type = "text/javascript";
if (script.readyState) {
script.onreadystatechange = function(){
if (script.readyState == "loaded" || script.readyState == "complete"){
script.onreadystatechange = null;
onload_function();
}
};
} else {
script.onload = function(){
onload_function();
};
}
script.src = url;
document.getElementsByTagName("head")[0].appendChild(script);
}
In Firefox and IE, you should be able to use window.onerror for this. You can take advantage of the fact that scripts run in the order they are listed in the HTML to wrap an error handler around just the facebook script:
<script>
// Remember old error handler, if there is one.
var oldOnError = window.onerror;
// Special error handler for facebook script
window.onerror = function(message, url, linenumber) {
// Potentially alert the user to problem
alert('Problem with facebook: ...');
// Return true to suppress default error handling
return true;
}
</script>
<!-- Load facebook script -->
<script src="http://connect.facebook.net/en_US/all.js"></script>
<script>
// Remove error handler for facebook script
window.onerror = oldOnError;
</script>

Loading scripts dynamically

I'm loading a few YUI scripts dynamically in my code in response to an Ajax request. The DOM and the page is fully loaded when the request is made - it's a response for an user event.
I add the <scripts> tag to head as children, but I stumbled in a few problems:
I add two YUI scripts hosted at the Yahoo! CDN and an inlined script of my own responsible for creating object, adding event listeners and rendering the YUI widgets. But I when my script run the YUI scripts are not loaded yet giving me errors and not running as I expect.
There's a way to only run my script (or define a function to be run) when YUI scripts are fully loaded?
Have you tried an onload event?
Edited:(thanks Jamie)
var script = document.createElement("script");
script.type = "text/javascript";
script.src = src;
//IE:
if(window.attachEvent && document.all) {
script.onreadystatechange = function () {
if(this.readyState === "complete") {
callback_function(); //execute
}
};
}
//other browsers:
else {
script.onload = callback_function; //execute
}
document.getElementsByTagName("head")[0].appendChild(script);
If you're using YUI 2.x I highly recommend using the YUI Get utility, as it's designed to handle just this sort of a problem.
If you are loading multiple individual script files from the Yahoo! CDN, you'll need to makes sure both are loaded before executing your dependent code. You can avoid this using the combo handler. See the Configurator to get what the script url should be to load both/all needed YUI files from one url.
http://developer.yahoo.com/yui/articles/hosting/
With that in mind, assuming you must load the YUI files asynchronously, you should use an onload/onreadystatechange handler as noted by digitalFresh.
I would recommend the following pattern, however:
(function (d) {
var s = d.createElement('script'),
onEvent = ('onreadystatechange' in s) ? 'onreadystatechange' : 'onload';
s[onEvent] = function () {
if (("loaded,complete").indexOf(this.readyState || "loaded") > -1) {
s[onEvent] = null;
// Call your code here
YAHOO.util.Dom.get('x').innerHTML = "Loaded";
}
};
// Set the src to the combo script url, e.g.
s.src = "http://yui.yahooapis.com/combo?2.8.1/...";
d.getElementsByTagName('head')[0].appendChild(s);
})(document);
You could use a setTimeout() to run some function that just checks if it's loaded - check something like
if (typeof YUI_NAMESPACED_THING !== "undefined") runCode()
EDIT Thanks, CMS
If I understand this correctly, your ajax response with this:
<script href="yui-combo?1"></script>
<script href="yui-combo?2"></script>
<p>some text here</a>
<script>
// using some of the components included in the previous combos
// YAHOO.whatever here...
</script>
If this is the case, this is a clear case in which you should use dispatcher plugin. Dispatcher will emulate the browser loading process for AJAX responses. Basically it will load and execute every script in the exact order.
Best Regards,
Caridy

Categories

Resources