I have an chrome extension, with 2 content script injected by manifest and one background script.
{
"manifest_version": 2,
"name": "Test",
"permissions": [
"tabs", "<all_urls>", "activeTab", "storage"
],
"content_scripts": [
{
"matches": ["http://*/*", "https://*/*"],
"js": [
"content/autofill/lib_generic.js",
"content/autofill/lib.js"],
"run_at": "document_end"
}
],
"web_accessible_resources": [
"content/specific_scripts/*"
],
"background": {
"scripts": ["background.js"],
"persistent": false
}
}
lib_generic.js contains one function named apply_forms(...) (its description is not important). The function is called from lib.js file. But this procedure doesn't work with several pages, so for each such page a I have a special script - also with only one function named apply_forms(...).
I have a function, which takes current domain as input and returns name of desired specific script or false if generic should be used.
There is too many files and it's logic is more complicated, so I can't just list all (url, script) pairs in "content_scripts" directive (I also don't want to inject all specific files as content script).
I've tried something like this in background (note that it's only for demonstration):
var url = ""; //url of current tab
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
if(changeInfo.status == "complete") {
var filename = getSpecificFilename(url);
chrome.tabs.executeScript(tabId, {file: filename}, function() {
//script injected
});
}
});
NOTE: getSpecificFilename(...) will always return a name
But I get Unchecked runtime.lastError while running tabs.executeScript: Cannot access a chrome:// URL on the 5th line.
Can anyone help me with this? Is it the good way to "override` function definition dynamically, or should I go different way (which one, then).
Thanks.
This means, probably, that you're getting an onUpdated event on an extension/internals page (popup? options page? detached dev tools?).
One option is to filter by URL:
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
if(changeInfo.status == "complete") {
if(!tab.url.match(/^http/)) { return; } // Wrong scheme
var filename = getSpecificFilename(url);
chrome.tabs.executeScript(tabId, {file: filename}, function() {
//script injected
});
}
});
Another (and probably better) option is to make your content script request this injection:
// content script
chrome.runtime.sendMessage({injectSpecific : true}, function(response) {
// Script injected, we can proceed
if(response.done) { apply_forms(/*...*/); }
else { /* error handling */ }
});
// background script
chrome.runtime.onMessage.addListener(function(message, sender, sendResponse) {
if(message.injectSpecific){
var filename = getSpecificFilename(sender.url);
chrome.tabs.executeScript(sender.tab.id, {file: filename}, function() {
sendResponse({ done: true });
});
return true; // Required for async sendResponse()
}
});
This way you know that a content script is injected and initiated this.
Related
I want to extract the HTML of a webpage so that I can analyze it and send a notification to my chrome extension. Sort of like how an adblocker does it when analyzing a web page for ads and then tell the extension how many possible ads there are.
I am trying to use the document object in content-scripts to get the HTML, however, I always seem to get the HTML of my popup file instead. Can anybody help?
content-script.js
chrome.tabs.onActivated.addListener(function(activeInfo) {
chrome.tabs.get(activeInfo.tabId, function(tab) {
console.log("[content.js] onActivated");
chrome.tabs.sendMessage(
activeInfo.tabId,
{
content: document.all[0].innerText,
type: "from_content_script",
url: tab.url
},
{},
function(response) {
console.log("[content.js]" + window.document.all[0].innerText);
}
);
});
});
chrome.tabs.onUpdated.addListener((tabId, change, tab) => {
if (tab.active && change.url) {
console.log("[content.js] onUpdated");
chrome.tabs.sendMessage(
tabId,
{
content: document.all[0].innerText,
type: "from_content_script",
url: change.url
},
{},
function(response) {
console.log("[content.js]" + window.document.all[0].innerText);
}
);
}
});
background.js
let messageObj = {};
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
// Arbitrary string allowing the background to distinguish
// message types. You might also be able to determine this
// from the `sender`.
if (message.type === "from_content_script") {
messageObj = message;
} else if (message.type === "from_popup") {
sendResponse(messageObj);
}
});
manifest.json
{
"short_name": "Extension",
"version": "1.0.0",
"manifest_version": 3,
"name": "My Extension",
"description": "My Extension Description",
"permissions": ["identity", "activeTab", "tabs"],
"icons": {
"16": "logo-16.png",
"48": "logo-48.png",
"128": "logo-128.png"
},
"action": {
"default_icon": "ogo_alt-16.png",
"default_popup": "popup.html"
},
"content_scripts": [
{
"matches": ["http://*/*", "https://*/*"],
"js": ["./static/js/content-script.js"],
"run_at": "document_end"
}
],
"background": {
"service_worker": "./static/js/background.js"
}
}
Your current content script is nonfunctional because content scripts cannot access chrome.tabs API. If it kinda worked for you, the only explanation is that you loaded it in the popup, which is wrong because the popup is not a web page, it's a separate page with a chrome-extension:// URL.
For your current goal, there's no need for the background script at all because you can simply send a message from the popup to the content script directly to get the data. Since you're showing the info on demand there's also no need to run the content scripts all the time in all the sites i.e. you can remove content_scripts from manifest.json and inject the code on demand from the popup.
TL;DR. Remove content_scripts and background from manifest.json, remove background.js and content-script.js files.
manifest.json:
"permissions": ["activeTab", "scripting"],
popup.html:
<body>
your UI
<script src=popup.js></script>
</body>
popup.js:
(async () => {
const [tab] = await chrome.tabs.query({active: true, currentWindow: true});
let result;
try {
[{result}] = await chrome.scripting.executeScript({
target: {tabId: tab.id},
func: () => document.documentElement.innerText,
});
} catch (e) {
document.body.textContent = 'Cannot access page';
return;
}
// process the result
document.body.textContent = result;
})();
If you want to to analyze the page automatically and display some number on the icon then you will need the background script and possibly content_scripts in manifest.json, but that's a different task.
This question already has answers here:
What is the difference between a function call and function reference?
(6 answers)
Closed 1 year ago.
So I'm trying to make an extension that allows you to write custom JS to be injected on any page for a given domain. My popup loads the saved JS code, and when clicking save, the JS is evaluated, and that works just fine. I can't figure out how to get the code to evaluate on page load, though.
Here is what I have so far.
//Content.js
//Globals
var entries = {"test": "test"}; //Entries dictionary "domain": "js code"
var url = window.location.href; //Full URL of the tab
var parts = url.split("/"); //URL split by '/' character
var domain = parts[2] + ''; //Just the domain (global)
loadChanges();
chrome.runtime.onMessage.addListener(listener);
window.onload=eval(entries[domain]); //doesn't work
function listener (request, sender, sendResponse) {
console.log("Manipulating data for: " + domain);
if (request == "LOAD"){
if(entries.hasOwnProperty(domain)){
console.log("PE - Loaded Value: " + entries[domain].toString());
sendResponse(entries[domain]);
} else {
console.log("Nothing to load");
sendResponse('');
}
} else {
entries[domain] = request;
console.log(entries[domain]);
saveChanges();
eval(request); //This one DOES work
}
}
//Load saved code (on startup)
function loadChanges() {
chrome.storage.local.get(['PE'], function (data){
console.log(data.PE);
if (data.PE == null){
return;
}
entries=data.PE;
});
if(entries.hasOwnProperty(domain)){
eval(entries[domain]); //doesn't work
}
}
//Save changes to code (on button press)
function saveChanges() {
chrome.storage.local.set({PE: entries}, function(data){
console.log("Saved Value: " + entries[domain])
});
}
Note the "doesn't work" comments in there.
manifest.json
{
"name": "PersistEdit",
"version": "0.1.1",
"manifest_version": 2,
"content_scripts":[
{
"matches": ["<all_urls>"],
"js": ["content.js"],
"run_at": "document_end",
"persistent": false
}
],
"background": {
"scripts": [
"background.js"
],
"persistent": false
},
"browser_action": {
"default_popup": "popup.html",
"default_title": "PersistEdit"
},
"permissions": [
"storage"
]
}
document.addEventListener('DOMContentLoaded', onload, false);
function onload(){
chrome.tabs.query({currentWindow: true, active: true}, function (tabs){
chrome.tabs.sendMessage(tabs[0].id, "LOAD");
});
}
Didn't include my popup.html or popup.js because those parts of it work as intended, but I can include them if necessary. I'm not sure what I'm missing here, any guidance would be appreciated.
window.onload is supposed to be a function.
Here window.onload=eval(entries[domain]); you are just assigning the result of eval to onload(which happens immediately during the assignment). It's possible that entries isn't properly populated at that time.
Try the following code
window.onload=function () {
eval(entries[domain]);
}
I'm tring to get current tab id in content script. But it fails all the time. I'm not sure, what i'm doing false.
Here are some solutions from another topics, but these are not working in my extension:
CODE 1 - content.js
chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
alert("sent from tab.id=", sender.tab.id);
});
CODE 2 - content.js
chrome.extension.sendRequest({
action: "WhatYouWant"
});
chrome.extension.onRequest.addListener(function (request, sender, sendResponse) {
if (request.action) {
alert('The response is : ' + request.action);
}
});
background.js
chrome.extension.onRequest.addListener(function (request, sender, sendResponse) {
if (request.action) {
// Make what you want
chrome.tabs.getSelected(null, function (tabs) {
chrome.tabs.sendRequest(tabs.id, {
action: "response"
});
});
}
});
manifest.json
...
"background": {
"scripts": ["background.js"],
"persistent": true
},
"content_scripts": [{
"all_frames": true,
"js": ["content.js"],
"matches": ["<all_urls>"],
"run_at": "document_end"
}],
"web_accessible_resources": [
"content.js"
],
...
Note: This is not duplicate topic, the solutions in other questions don't work for me.
The chrome.tabs API is not accessible inside a content script:
Use simply (javascript) :
location.href
to get the URL of the current Web page.
chrome.tabs.query() function returns tabs Array.
refer to this document:
https://developer.chrome.com/extensions/tabs#method-query
when i tried tabs.query() function, the callback function returns all current tabS array.
chrome.tabs.query( {}, function(cbResult){
console.log(cbResult);});
with google's document and above call back returns, you can find the proper tab is using first query() argument.
if the first argument is Null, then query function returns all tabs array.
you can narrow the candidate tabs using the first argument.
Screenshot
I use pageMod() in my Firefox SDK app, and i want to convert to Webextension.
I declare on top on my background js file:
var pageMod = require("sdk/page-mod");
I have undesrtood in Webextension is declared in manifest.json
"content_scripts": [
{
"matches": ["*://*.mytestsite.com/*"],
"js": ["background.js"]
}
]
But i call pageMod() inside my background script and i have a lot of attachment and other. I dont know how i can convert this.
For example what i want convert:
pageMod.PageMod({
include: "*.mytestsite.com",
contentScriptFile: [self.data.url("jquery-2.0.3.min.js"), self.data.url("jquery-ui.min.js"), self.data.url("site_modal_min.js"), self.data.url("timerreview.js")],
onAttach: function onAttach(worker) {
How use my PageMod() call with Webxtension
Please note that in manifest.json, you cannot have background.js inside of a content_scripts tag. You would instead do it like this (assuming your match selector for the domain is correct):
"background": {
"scripts": ["background.js"]
},
"content_scripts": [
{
"matches": ["*://*.mytestsite.com/*"],
"js": ["timerreview.js", "jquery.js"]
}
]
If you want to communicate (variables, etc) use messaging between the background script and the content script. This is especially useful since most of the WebExtensions API is only available for background scripts.
In the content script, add:
// Listen for messages from the background script
browser.runtime.onMessage.addListener(onMessage);
function onMessage(message) {
switch(message.action)
case "testAction":
testAction(message.data);
break;
default:
break;
}
}
function sendMessage(action, data){
browser.runtime.sendMessage({"action": action, "data": data});
}
In the background script, add:
// listen for messages from the content or options script
browser.runtime.onMessage.addListener(function(message) {
switch (message.action) {
case "actionFromContent":
doSomething(); // message.data is not needed
break;
default:
break;
}
});
// See also https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/Tabs/sendMessage
function sendMessage(action, data){
function logTabs(tabs) {
for (tab of tabs) {
browser.tabs.sendMessage(tab.id, {"action": action, "data": data}).catch(function(){
onError("failed to execute " + action + "with data " + data);
});
}
}
browser.tabs.query({currentWindow: true, active: true}).then(logTabs, onError);
}
function onError(error){
console.error(error);
}
Now you can use:
sendMessage("actionFromContent") from within the content script to send a message to the background script
sendMessage("testAction", "someData") from within the background script to the active content script (the active tab)
I am writing my First Chrome Plugin and I just want to get some text present on the current webpage and show it as a alert when i click the Extension. Lets say I am using any any webpage on www.google.com after some Search query, Google shows something like "About 1,21,00,00,000 results (0.39 seconds) " . I want to show this Text as an alert when i execute my plugin. This is what i am doing.
here is the manifest.json that i am using
{
"manifest_version": 2,
"name": "Getting started example",
"description": "This extension shows a Google Image search result for the current page",
"version": "1.0",
"background": {
"persistent": false,
"scripts": ["background.js"]
},
"content_scripts": [{
"matches": ["*://*.google.com/*"],
"js": ["content.js"]
}],
"browser_action": {
"default_icon": "icon.png",
"default_popup": "popup.html"
},
"permissions": [
"activeTab"
]
}
Here is my popup.js
document.addEventListener('DOMContentLoaded', function() {
document.getElementById("checkPage").addEventListener("click", handler);
});`
function handler() {
var a = document.getElementById("resultStats");
alert(a.innerText); // or alert(a.innerHTML);
}
Here is my content.js
// Listen for messages
chrome.runtime.onMessage.addListener(function (msg, sender, sendResponse) {
// If the received message has the expected format...
if (msg.text === 'report_back') {
// Call the specified callback, passing
// the web-page's DOM content as argument
sendResponse(document.all[0].outerHTML);
}
});
Here is my background.js
var urlRegex = /^https?:\/\/(?:[^./?#]+\.)?google\.com/;
// A function to use as callback
function doStuffWithDom(domContent) {
console.log('I received the following DOM content:\n' + domContent);
}
// 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);
}
});
1) run content scrip after document ready ** check "run_at"
"content_scripts": [{
"run_at": "document_idle",
"matches"["*://*.google.com/*"],
"js": ["content.js"]
}],
2) on click of extension make another js to run( popup js). The popup js has access to the ( open page document)
// A function to use as callback
function doStuffWithDom() {
//console.log('I received the following DOM content:\n' + domContent);
//get tabID when browser action clicked # tabId = tab.id
chrome.tabs.executeScript(tabId, {file: "js/popup.js"});
}
3) In popup JS simple you can set alert
var a = document.getElementById("resultStats");
alert(a.innerText); // or alert(a.innerHTML);
Just remove "default_popup" part in manifest.json, as you have listened to chrome.browserAction.onClicked event in background page. They can't live at the same time.