Chrome Extension content_script at both document_start and document_end - javascript

Good day to everyone, as you can see I'm pretty new at Chrome extensions.
Can you run a script from content_scripts before and after the the DOM or page fully loads?
Like:
"content_scripts": [ {
"matches": ["<all_url>"],
"js": ["content.js"],
"all_frames": true,
"run_at": "document_start",
"run_at": "document_end"
} ]
Or something like:
"content_scripts": [ {
"matches": ["<all_url>"],
"js": ["content1.js"],
"all_frames": true,
"run_at": "document_start"
} ],
"content_scripts": [ {
"matches": ["<all_url>"],
"js": ["content2.js"],
"all_frames": true,
"run_at": "document_end"
} ]

You can have only one content_scripts entry, so it would be like:
"content_scripts": [{
"matches": ["<all_urls>"],
"js": ["content1.js"],
"all_frames": true,
"run_at": "document_start"
},{
"matches": ["<all_urls>"],
"js": ["content2.js"],
"all_frames": true,
"run_at": "document_end"
}]
With this setting, content1.js would run at the beginning and content2.js at the end.

You are going in the right direction:
You can use the run_at property, but your manifest needs to look something like this:
"content_scripts": [
{
"matches": ["<all_url>"],
"js": ["content1.js"],
"all_frames": true,
"run_at": "document_start"
},
{
"matches": ["<all_url>"],
"js": ["content2.js"],
"all_frames": true,
"run_at": "document_end"
}]
It should be an array of objects defining all content scripts instead to declaring the content_script property twice.
If you are using jQuery you can also use $(document).ready() like this:
const func1 = params => {
// Stuff that will happen as soon as it can
};
$(document).ready(() => {
// stuff that will happen when DOM is ready
});
If you are not using jQuery you can just do a quick google search to find alternatives for $(document).ready in vanilla js.
Other solution you can use it to inject content script programmatically from background script using chrome.tabs.executeScript() docs.

Related

how to use another JS file in content script's JS file

I had defined a content script in the manifest.json as:
"content_scripts": [
{
"matches": [
"https://*/*"
],
"js": [
"cs.js"
]
}
]
And in cs.js I would like to load another JS (mark.js) as module:
// cs.js
import Mark from './lib/mark.es6.min.js'
window.onload = (event) => {
console.log(Mark)
}
and when I am visiting any page, there is an error in the console
Uncaught SyntaxError: Cannot use import statement outside a module
The directory structure looks like:
manifest.json
cs.js
lib/mark.es6.min.js
What is the best way in which I can load the mark.js to be able to use in my content script?
Not exactly what you asked for, but be aware that you can spawn different scripts from the background page:
chrome.tabs.executeScript(tabId, {
file: "./content_scripts/script1.js",
});
chrome.tabs.executeScript(tabId, {
file: "./content_scripts/script2.js",
});
And then, the functions you declare in script1.js can be used in script2.js
Thanks to 1j01, woxxom and nicola-scionti for good suggestions (Can I share code between different parts of Chrome Extension?, How to import ES6 modules in content script for Chrome Extension and https://stackoverflow.com/a/70359345/725306 respectively).
I ended up using content_scripts only as:
"content_scripts": [
{
"matches": [
"https://*/*"
],
"js": [
"lib/mark.es6.min.js",
"cs.js"
]
}
]
and then simply use that in my cs.js
// cs.js
window.onload = (event) => {
console.log(Mark)
}

Uncaught ReferenceError: $ is not defined in chrome extension content script

I'm trying to load jQuery into content script when page is loaded.
All works great until couple days ago when some users started to get errors.
Uncaught ReferenceError: $ is not defined
I was thinking what can cause this problem, because all works great.
Part of manifest file:
"content_scripts": [
{
"all_frames": true,
"js": [
"js/jquery-3.6.0.min.js"
],
"css": [
"css/overlay.css"
],
"matches": [
"https://coolsite.com/*"
],
"runAt": "document_start"
},
{
"all_frames": true,
"js": [
"content/content1.js"
],
"matches": [
"https://coolsite.com/*",
],
"runAt": "document_end"
}]
Code of content1.js
$(document).ready(function () {
// Broadcast runner loaded
chrome.runtime.sendMessage({
callback: true,
runnerLoaded: true
});
});
The problem is something about jQuery, couldn't find out why.
I will be so thankful for some help.
Thank you,
Ori

How to override functions from the Web API when writing a Firefox addon?

For research purposes, I am trying to write a web-extension that override some part of the web APIs (for example setItem, getItem, or removeItem from the Storage interface).
Unfortunately, when trying the following:
index.js
Storage.prototype.setItem = function(a, b) { return 42; }
manifest.json
{
"manifest_version": 2,
"name": "cool-extension-name",
"version": "1.0",
"description": "A useful description.",
"icons": {"48": "icons/potatoes.jpg"},
"content_scripts": [{
"run_at": "document_start",
"matches": ["<all_urls>"],
"js": ["index.js"]
}]
}
Then using web-ext run, and opening Firefox's WebConsole after loading any webpage.
$ window.localStorage.setItem(1, 2)
undefined
I would have hoped it to return 42.
What happened?
You need to run the code via window.eval() to access the context of the web page itself. For example:
browser.tabs.executeScript(1, {
code: 'window.eval("Storage.prototype.setItem = function(a, b) { return 42; }")'
})
See also:
https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Content_scripts#Using_eval_in_content_scripts

Chrome Extension Javascript not working properly

I'm trying to write Chrome Extension in pure javascript to fetch a collection of nodes and make some changes to it. So, I started off with:
var list = document.getElementsByClassName("testclass");
console.log(list); //1
console.log(list.length); //2
console.log(list[0]); //3
The output for this comes up as:
1. An object [item:function, namedItem:function], which has all the elements and a length: <number of elements> and __proto___: HTMLCollection (As Expected)
2. 0
3. undefined
The same thing, which I give in jsfiddle, I get proper outputs:
1. HTMLCollection object with all elements and length: <no of elements> and __proto__: HTMLCollection
2. <no of elements>
3. First Element
Why am I unable to iterate when the javascript used is them same? Is there something else to do when we are using a Chrome extension?'
Heres my manifest.json in case it helps:
{
"manifest_version": 2,
"name": "Something",
"description": "Something something...",
"version": "1.0",
"permissions": [
"tabs",
"notifications",
"http://*/",
"https://*/"
],
"content_scripts": [{
"matches": ["https://*","http://*"],
"js": ["testscript.js"],
"run_at": "document_end"
}],
"web_accessible_resources": ["samplejs.js"],
"browser_action": {
"default_popup": "popup.html"
}
}
My javascript code is in testscript.js
Well, I did some modifications in your manifest file and it worked for me. Here is the updated version:
{
"manifest_version": 2,
"name": "Something",
"description": "Something something...",
"version": "1.0",
"permissions": [
"tabs",
"notifications",
"http://*/",
"http://*/*",
"https://*/*"
],
"content_scripts": [{
"matches": ["https://*/*", "http://*/*"],
"js": ["testscript.js"],
"run_at": "document_end"
}],
"web_accessible_resources": ["samplejs.js"],
"browser_action": {
"default_popup": "popup.html"
}
}
I have changed the permissions and matches sections.
Found a solution. The problem was that the DOM was not getting loaded fully, hence my script which executed before DOM got loaded, could not fetch data properly is what I am guessing.
I tried using
document.onload=function(){}
and
window.onload=function(){}
window.onload had trouble fetching the output, and document.onload never triggered at all.
So, tried using a delay:
setTimeout(function(){
<javascript code>
},3000);
And this works fine. Not sure if its the best method.
As an alternative, added jquery to the extension and used:
$(document).ready(function(){
<javascript code>
});
And this works even better.

How to specify which content scripts which will run on all_frames and which won't?

When specifying parameters in the manifest file of a chrome extension there is an option all_frames. This allows the content scripts to be embedded in all frames of a page or not.
An example of what I want to achieve is have a.js running with all_frames=false and b.js with all_frames=true.
The content_scripts manifest property is an array, so you can define multiple content script specification objects:
"content_scripts": [
{
"matches": ["http://www.google.com/*"],
"css": ["mystyles.css"],
"js": ["a.js"],
"all_frames": false
},
{
"matches": ["http://www.yahoo.com/*"],
"js": ["b.js"],
"all_frames": true
}
],

Categories

Resources