I don't want to include jQuery on every page by listing it in the manifest
In the console, this works fine, but I can't dynamically include jQuery in a content script
No idea why
Put these two files in a folder (content.js and manifest.json)
Go to chrome:extensions in omnibox (url bar)
Load Unpacked Extension
Select Folder
Go to any page and CMD+Shift+R reload without cache
Check out the console and see jQuery is undefined
content.js
if (document.readyState === "complete") {
appendJQuery();
} else {
document.addEventListener("DOMContentLoaded", appendJQuery);
} function appendJQuery () {
var jq = document.createElement("script");
window.document.querySelector("head").appendChild(jq);
jq.onload = function () {
console.log(typeof $); // $ is not defined ?????
}
jq.src = "https://code.jquery.com/jquery-2.1.1.min.js";
}
manifest.json
{
"manifest_version": 2,
"name": "Sample",
"short_name": "Sample",
"version": "1.1",
"permissions": ["tabs", "http://*/*, https://*/*", "*://*/*", "<all_urls>"],
"content_scripts": [{
"matches": ["*://*/*", "http://*/*", "https://*/*", "file://*/*"],
"js": ["content.js"],
"run_at": "document_start"
}]
}
then jQuery is undefined......... wtf??? anyone know why??
Related
my aim is to override the default setInterval JS function in all pages with a browser extension (by injecting my javascript code just at the beginning of pages). This script should run before any other script, jQuery included. My latest attempt (not working) is this:
manifest.js
...
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["content.js"],
"run_at": "document_start",
"all_frames": true
}
],
"background": {
"service_worker": "background.js"
},
"permissions": [
"scripting"
],
"host_permissions": ["<all_urls>"],
...
content.js
function do_newInterval() {
window.setInterval = function setInterval() {console.log('hello')}
};
chrome.runtime.onMessage.addListener(function(msg, sender, sendResponse) {
if (msg.type === 'newInterval') { do_newInterval(); }
});
background.js
chrome.tabs.onUpdated.addListener(async () => {
let activeTab=await chrome.tabs.query({ active: true,
lastFocusedWindow: true })
chrome.tabs.sendMessage(activeTab[0].id, { type: "newInterval"
});
})
Unfortunately the code to replace the setInterval function is run after other page scripts are executed!
Could you please show me the right way to make my JS run as first?
Thank you!
I'm trying to send new tab URL from background.js to content.js. The .sendMessage() performs but doesn't get to the content.js
background.js:
chrome.tabs.onUpdated.addListener(
function (tabId, changeInfo, tab) {
if (changeInfo.url) {
chrome.tabs.sendMessage(tabId, {
url: changeInfo.url
})
}
}
);
content.js:
chrome.runtime.onMessage.addListener(function(request, sender, callback) {
console.log('here');
});
manifest.json:
{
"manifest_version": 2,
"name": "Url tracker",
"description": "Track your latest visited URLs",
"version": "0.0.1",
"icons": {
"16": "logo-small.png",
"48": "logo-small.png",
"128": "logo-small.png"
},
"permissions": [
"activeTab",
"tabs"
],
"background": {
"scripts":["background.js"],
"persistent": false
},
"content_scripts": [{
"matches": ["<all_urls>"],
"all_frames": true,
"js": ["content.js"]
}]
}
Content scripts run after DOMContentLoaded by default (it can be changed) but the URL is reported to onUpdated when the tab starts loading i.e. before the content script runs.
The solution is to add a check to skip the first update because it's not needed: an instance of the content script runs in each matching web page and it already knows location.href of its page.
if (changeInfo.url && tab.status === 'complete') {
After you reload your extension on chrome://extensions page (or update it from the web store), all its content scripts get "orphanized" and can't receive messages.
The solution is to re-inject them explicitly.
I try to send message from an iframe loaded from my extension to my extension (background script or content script).
The created Iframe is loaded from the extension via a content script.
I am searching for a way to communicate but all my attempts failed...
Manifest.json
{
"author": "***********",
"background": {
"page": "back/background.html",
"persistent": true
},
"browser_action": {
"default_title": "***",
"default_popup": "./popup/popup.html"
},
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["./content/allurls.js"],
"all_frames":true
},
{
"matches": ["<all_urls>"],
"js": ["./banner/confirm_banner.js"]
}
],
"web_accessible_resources": [
"frame.html"
],
"description": "oui",
"manifest_version": 2,
"name": "***",
"permissions": ["tabs"],
"version": "1.0"
}
Confirm_banner.js (load the iframe)
var extensionOrigin = 'chrome-extension://' + chrome.runtime.id;
window.onload = load_iframe();
function load_iframe()
{
if (!location.ancestorOrigins.contains(extensionOrigin))
{
var iframe = document.createElement('iframe');
iframe.src = chrome.runtime.getURL('../frame.html');
iframe.style.cssText = 'position:fixed;top:0;left:0;display:block;' +
'width:100%;height:40px;';
document.body.appendChild(iframe);
}
}
Frame.js (script linked with frame.html)
$(document).ready(function()
{
$('#jamais').click(function()
{
send_message("BANNER", "jamais");
alert("send");
});
});
function send_message(type, data)
{
var msg = {
type: type,
data: data
};
window.postMessage(msg, "*");
}
Handler in allurls.js (content script)
window.addEventListener('message', function(event) {
if (event.data.type && (event.data.type === 'BANNER'))
{
alert("ouimonsieur");
}
});
So the message from iframe.js is well sent (prooved by the alert) but the content script recieve nothing from it, even before the :
if (event.data.type && (event.data.type === 'BANNER'))
Can someone see what is wrong or what other message passing protocol i can use (i also tried with top.window.postmessage) ?
Ty wOxxOm for the answer, i was close :
just replace window by parent in frame.js and all works perfectly.
Because even if the content script run in iframe,
Frame.js is not a content script, it's a iframe script and runs in the context of the extension.
I'm trying to access some DOM elements from a webpage:
<html>
<button id="mybutton">click me</button>
</html>
I want to access the innerHTML ("click me") through a chrome extension:
chrome.browserAction.onClicked.addListener(function(tab) {
var button = document.getElementById("mybutton");
if(button == null){
alert("null!");
}
else{
alert("found!");
}
});
When I click the extension, the popup says: "null".
My manifest.json:
{
"name": "HackExtension",
"description": "Hack all the things",
"version": "2.0",
"permissions": [
"tabs", "http://*/*"
],
"background": {
"scripts": ["contentscript.js"],
"persistent": false
},
"browser_action": {
"scripts": ["contentscript.js"],
"persistent": false
},
"manifest_version": 2
}
The solution:
You need a manifest file, a background script and a content script. This is not really clear in the documentation that you have to use it and also, how to use it. For alerting the full dom, see here. Because I have a hard time finding a complete solution that actually works and not just snippets that are useless for newbies, like me, I included a specific solution:
manifest.json
{
"manifest_version": 2,
"name": "Test Extension",
"version": "0.0",
"background": {
"persistent": false,
"scripts": ["background.js"]
},
"content_scripts": [{
"matches": ["file:///*"],
"js": ["content.js"]
}],
"browser_action": {
"default_title": "Test Extension"
},
"permissions": ["activeTab"]
}
content.js
/* Listen for messages */
chrome.runtime.onMessage.addListener(function(msg, sender, sendResponse) {
/* If the received message has the expected format... */
if (msg.text && (msg.text == "report_back")) {
/* Call the specified callback, passing
the web-pages DOM content as argument */
sendResponse(document.getElementById("mybutton").innerHTML);
}
});
background.js
/* Regex-pattern to check URLs against.
It matches URLs like: http[s]://[...]stackoverflow.com[...] */
var urlRegex = /^file:\/\/\/:?/;
/* A function creator for callbacks */
function doStuffWithDOM(element) {
alert("I received the following DOM content:\n" + element);
}
/* When the browser-action button is clicked... */
chrome.browserAction.onClicked.addListener(function(tab) {
/*...check the URL of the active tab against our pattern and... */
if (urlRegex.test(tab.url)) {
/* ...if it matches, send a message specifying a callback too */
chrome.tabs.sendMessage(tab.id, { text: "report_back" },
doStuffWithDOM);
}
});
index.html
<html>
<button id="mybutton">click me</button>
</html>
Just save the index.html somewhere and load in the folder as an extension, containing the three other files. Open the index.html and push the extension button. It should show "click me".
Starting with Manifest V3, your content scripts won't be able to access anything generated by other loaded scripts and using a trick like inlining a your code inside <script> tag won't work due to stricter CSP rules. This caused me a lot of head ache since I couldn't figure out how to access library-generated DOM properties similar to React or Redux DevTools.
Instead, you have to now inject your script inside the service_worker with eg:
chrome.scripting.registerContentScripts([
{
id: 'inject',
matches: ['<all_urls>'],
js: ['inject.js'],
runAt: 'document_end',
world: 'MAIN'
}
])
Notice the 'MAIN' property, not the default 'ISOLATED'. Then inside my inject.js I do whatever, eg:
window.addEventListener('load', () => {
findReact()
})
Also you have to add the script to the manifest.json:
"web_accessible_resources": [
{
"resources": ["inject.js"],
"matches": ["<all_urls>"],
"extension_ids": []
}
],
"externally_connectable": {
"ids": ["*"]
},
Not sure is "externally_connectable" needed. And you need to add at least "scripting" permissions. I used the React DevTools migration as my source https://github.com/facebook/react/pull/25145
How to get access to variable app from content script app.js in background script background.js?
Here is how I try it (background.js):
chrome.tabs.executeScript(null, { file: "app.js" }, function() {
app.getSettings('authorizeInProgress'); //...
});
Here is what I get:
Here is manifest.json:
{
"name": "ctrl-vk",
"version": "0.1.3",
"manifest_version": 2,
"description": "Chrome extension for ctrl+v insertion of images to vk.com",
"content_scripts": [{
"matches": [
"http://*/*",
"https://*/*"
],
"js": ["jquery-1.9.1.min.js"
],
"run_at": "document_end"
}],
"web_accessible_resources": [
"jquery-1.9.1.min.js"
],
"permissions" : [
"tabs",
"http://*/*",
"https://*/*"
],
"background": {
"persistent": false,
"scripts": ["background.js"]
}
}
Full code for instance, at github
https://github.com/MaxLord/ctrl-vk/tree/with_bug
To avoid above error use following code
if (tab.url.indexOf("chrome-devtools://") == -1) {
chrome.tabs.executeScript(tabId, {
file: "app.js"
}, function () {
if (app.getSettings('authorizeInProgress')) {
alert('my tab');
REDIRECT_URI = app.getSettings('REDIRECT_URI');
if (tab.url.indexOf(REDIRECT_URI + "#access_token") >= 0) {
app.setSettings('authorize_in_progress', false);
chrome.tabs.remove(tabId);
return app.finishAuthorize(tab.url);
}
} else {
alert('not my');
}
});
}
instead of
chrome.tabs.executeScript(null, {
file: "app.js"
}, function () {
if (app.getSettings('authorizeInProgress')) {
alert('my tab');
REDIRECT_URI = app.getSettings('REDIRECT_URI');
if (tab.url.indexOf(REDIRECT_URI + "#access_token") >= 0) {
app.setSettings('authorize_in_progress', false);
chrome.tabs.remove(tabId);
return app.finishAuthorize(tab.url);
}
} else {
alert('not my');
}
});
Explanation
chrome://extensions/ page also fires chrome.tabs.onUpdated event, to avoid it we have to add a filter to skip all dev-tool pages.
(Would've submitted this as comment to the accepted answer but still lack the required reputation)
You should also give the tabId to chrome.tabs.executeScript as first argument when you have it. Otherwise you risk user switching windows/tabs right after requesting a URL and background.js doing executeScript against wrong page.
While fairly obvious on hindsight it threw me for a loop when I got that same error message "Cannot access contents of url "chrome-devtools://.." even though my chrome.tabs.onUpdated eventhandler was checking that the page user requested had some specific domain name just before doing the executeScript call.
So keep in mind, chrome.tabs.executeScript(null,..) runs the script in active window, even if the active window might be developer tools inspector.
We should notice that, in the manifest cofigļ¼
"content_scripts": [{
"matches": [
"http://*/*",
"https://*/*"
],
"js": ["jquery-1.9.1.min.js"
],
in the "matches" part, only http, https are matched, so if you load your extension in page like: 'chrome://extensions/', or 'file:///D:xxx', that error will occur.
You may load your extension in the page with the url 'http://'; or add more rules in your 'matches' array.