I am writing up a ContentScript for a Chrome Extension. In the script, I want to import a javascript (actually a JS and a CSS) and initiate a class from that script.
To ensure the Javascript is properly loaded, I followed the guidance from several SO posts like this, this and this.
var link = document.createElement( "link" );
document.body.appendChild(link);
link.onload = function(){
console.log("link loaded");
var script = document.createElement('script');
document.body.appendChild(script); //or something of the likes
script.onload = function () {
...
console.log("script loaded");
// somehow ContentTools is undefined even after the script is loaded
editor = ContentTools.EditorApp.get();
editor.init('*[data-editable]', 'data-name');
console.log("all finished")
};
script.src = "https://....min.js";
};
link.href = "https://....min.css";
link.type = "text/css";
link.rel = "stylesheet";
I am assuming that this way of callling ContentTools.Editor at the innermost of the nested onload is a good way of guarantee that the proper CSS and JS dependencies are properly loaded. However, when I run it as a Chrome Extension inside the contentscript, it errors out with Undefined ContentTools as referenceerror but I saw message "link loaded" and "script loaded".
Clearly, ContentTools are loaded and defined (see the screenshot). And if I execute the whole script directly in the console, everything works. Looks like the root cause is only in Chrome Extension content script, somehow the editor = ContentTools... got called before the script is fully loaded.
As it is only an error as a Chrome extension but not in the Console, I am a bit lost here. jQuery related solution is also welcome here.
Related
Recently In my chrome, every page loads https://s3.amazonaws.com/exthub/e/2/r/US_chrome.js?cached=true and it is really annoying. I want to find which extension added such code snippets. But in the Network panel of Chrome devtools, the Initiator just shown VM***, I could not find which script invoke it even I set some break points in the scripts.
I have a lot of chrome extension, so it was difficult to check echo extension one by one. I also tried to search some keyword like content_scripts,executeScript,amazonaws and so on in %AppData%\..\Local\Google\Chrome\User Data\Default\Extensions, but I still could not find it.
Is there any convenient methods for finding the source script.
The VM*** script is the following.
(() => {
if (document.querySelector('script[data-awssuidacr]') !== null) {
return;
}
const head = document.querySelector('head');
const script = document.createElement('script');
script.type = 'text/javascript';
script.src = 'https://s3.amazonaws.com/exthub/e/2/r/US_chrome.js?cached=true';
script.dataset['awssuidacr'] = 'KMyretRSNnBnMx4zVMxXIXOlCwtj9scH';
head.appendChild(script);
})();
#dorian Thanks for your advice. Now I tried to disable the "YouTube Video Downloader", and the scripts did not show up again.
I did not find the extension by searching some keywords was because I forgot the unpacked extension, they are not in the %AppData%\..\Local\Google\Chrome\User Data\Default\Extensions directory. And I go through the code and find the js code was obfuscated.
I thought everything was well with my extension, it was loading the js libraries successfully on most of the domains I've tested my extension on, then I found a few domains where it seemed the libraries wasn't being loaded.
I am using the Google Web Font Loader library, and including it in my contentscript.js, which injects it to the page like this:
// Add Google Webfont Library
var gwebfont = document.createElement("script");
gwebfont.src = "https://ajax.googleapis.com/ajax/libs/webfont/1.6.26/webfont.js";
var callback = function (){
var s = document.createElement('script');
s.type = 'text/javascript';
var code = "WebFont.load({google: { families: ['Open Sans', 'Open Sans:light', 'Open Sans:semi-bold', 'Open Sans:bold'] } });";
try {
s.appendChild(document.createTextNode(code));
shadow.appendChild(s);
} catch (e) {
s.text = code;
shadow.appendChild(s);
}
}
gwebfont.onload = callback;
shadow.appendChild(gwebfont);
Yes, I do have the folder set in web_accessible_resources and the contentscript.js is also running..
On the few domains this doesn't work, it says WebFont is not defined, but on most of the domains I've tested this on, it works without any problem.
When I check in the Network tab in Chrome, I can see the webfont.js was loaded.
Can anyone tell me why only some domains give me the not defined error, when it appears the library was loaded?
I have also tried to run the code again in the console, and on the few domains where it doesn't work, it gives me the same not defined error.
This code doesn't work on hungry.dk, works on stylepit.dk
Here's a snippet of the event
Let me know if u want a closer look at something in here..
This question already has answers here:
The Chrome extension popup is not working, click events are not handled
(2 answers)
Closed 5 years ago.
Main Question
I'm building a Chrome extension that involves appending an iframe to the page. The extension makes an AJAX call in the background page to retrieve the URLs of the external CSS / JS to include and so I need to dynamically add these resources to the iframe. The iframe code is an HTML page that's included in the extension's web_accessible_resources (it has a URL of chrome-extension://...)
This is what I do in the iframe:
window.onload = function() {
chrome.runtime.sendMessage({action: "getResources"}, function(response) {
// get urls of css / js files
let { css, js } = response; // css and js are arrays of urls retrieved
let head = document.getElementsByTagName('head')[0];
for (let i = 0; i < css.length; i++) {
let link = document.createElement('link');
link.href = css[i];
link.type = "text/css";
link.rel = "stylesheet";
head.appendChild(link);
}
for (let i = 0; i < js.length; i++) {
let script = document.createElement('script');
script.src = js[i];
script.type = "text/javascript";
script.onload = function() {
window.alert('loaded script');
}
head.appendChild(script);
}
}
}
After this, I can log the stylesheets and scripts from the head so they are appended successfully. However, the callback in the script.onload is not called and I tried using getComputedStyle like in this answer to check the CSS and that didn't work. So both the stylesheets and scripts are not actually being executed.
I'm wondering if Chrome extensions just don't allow dynamic injections into iframes.
Additional Info
Here are some things I tried:
Adding the URLs to the permissions
Mentioned here. All the scripts I load are from sites outlined in the permissions field of the manifest.json file so CORS shouldn't be a problem.
Trying hacky ways to load the script
I tried script.innerHTML = "console.log('success')" and also tried head.innerHTML = '<scr' + 'ipt type="text/javascript" src="[URL_TO_LOAD]"></scr' + 'ipt>'. Neither worked. I didn't try any solutions with document.write or eval just because I would never use those even if they worked for security reasons.
SetTimeout
I tried setting a timeout after loading the script in case it took a while to execute but even after 10 seconds, global variables that should've been there are still undefined.
HTTPS
Requesting scripts served over HTTPS also did not work.
Any help would be appreciated.
Turns out it's due to Chrome's Content Security Policy. I found the answer here. However the scripts that can be dynamically injected into the iframe must be either local files or served over HTTPS.
I tried to write a bookmarklet which depends on another script. For this my bookmarklet includes a function like this:
function load(url, callback) {
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = url;
script.async = false;
script.onload = callback;
document.head.appendChild(script);
}
The callback function is called on most pages, but on some pages I get the following exception in the console and the script is not loaded (facebook.com is one example).
Component returned failure code: 0x80040111 (NS_ERROR_NOT_AVAILABLE)
An example of a bookmarklet:
javascript:function%20load(url,callback){var%20script=document.createElement('script');script.type='text/javascript';script.src=url;script.onload=callback;document.head.appendChild(script);}load('http://code.jquery.com/jquery-1.11.1.min.js',function(){console.log('Loaded');})
I use FireFox 29 on Ubuntu 12.04. If it is executed a "Loaded" should a appear in the console. On the first load an additional access to the resource is visible in the console. But as written above on e.g. facebook.com nothing happens at all. Neither is the script loaded nor is the callback called.
It was quite likely due to XSS from the bookmarklet itself. FF 35.0 doesn't have an error anymore and shows a well formatted message.
I recently found out about load.js, but I can't seem to find any indication of whether or not this is possible... (Note: I can't find a 'load.js' tag..)
I've got load.js successfully loading all my JS files, so I know it works. Has anyone got it working for loading CSS files as well?
Update: remyabel's solution worked perfectly for loading the physical files, but it seems there are a few quirks to this process...
For some reason, the order in which the CSS files are loaded and whether they're all done in one load(file1,file2); or in stages with load(file1).then(file2); seems to affect how the style rules are applied to the markup. I'm going to set up a few test cases on my local machine to try work out how or why this happens, but for now at least the files are being loaded.
Final Note:
Following on from the solution posted below, I've decided to use head.appendChild(script); instead of head.insertBefore(script, head.firstChild); to add the CSS elements to the DOM (still uses the original method for JS files).
This doesn't affect the order in which files are fetched and processed, but it makes Load.js insert my CSS links in the same order they were listed and at the end of the header instead of the beginning.
Direct from the source code
function asyncLoadScript(src) {
return function (onload, onerror) {
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = src;
My suggestion is to modify the script (which doesn't seem to contain much) to mirror the function but for a link tag, rather than a script tag.
to reflect OP's comment
The script is built on top of chain.js so it may be more complicated than expected.
Unless you want something else, I'm pretty sure what I wrote above is what you need to change, so it would look like:
function asyncLoadScript(src) {
return function (onload, onerror) {
// Get file extension
var ext = src.split('.').pop();
if (ext == "js")
{
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = src;
} else if (ext == "css")
{
var script = document.createElement('link');
script.type = 'text/css';
script.href = src;
script.rel = "stylesheet";
}
Theoretically that should work. Make another comment if it doesn't work.