Chrome extension: Add content (enable/disable icon) - javascript

I am writing an extension for Google Chrome and I am trying to add content in my current page when the user clicks on the add-on icon.
I would like to add the possibility to enable/disable the extension and to show/hide my injected content for every page.
manifest.json
"content_scripts": [
{
"matches": [
"http://*/*",
"https://*/*"
],
"css": ["css/style.css"],
"js": [
"js/content.js"
]
}
]
I do not see how to add content only for the page where the icon has clicked because with that, every page has the script.
I also tried something with the background script without success.
Do you have any ideas?
Thanks!

I had built the same feature for my chrome extension.
This will create an on/off switch / toggle (so many names when searching google to solve this :) )
I use the massaging between app and content script in the following method:
manifest
inserting my content script on all page (hover.js) and running my extension script (background.js)
....
"browser_action": {
"default_icon": {
"19": "icons/icon-active.png"
}
},
"content_scripts": [
{
"matches": ["<all_urls>"],
"css": ["css/hover.css"],
"js": ["js/hover.js"]
}
],
"background" : { "scripts": ["js/background.js"] },
....
background.js
Here we are preparing the background script (which run on all chrome windows) to send and receive extension status
// start extension as active
var status = true;
// set toggle of extension on browser action click and notify content script
chrome.browserAction.onClicked.addListener(function(tabs) {
if (status == 'true'){
status = false;
chrome.browserAction.setIcon({path: "icons/16x16.png"});
}
else{
status = true;
chrome.browserAction.setIcon({path: "icons/icon-active.png"});
}
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.tabs.sendMessage(tabs[0].id, {status: status});
});
});
// check status on tab update and notify content script
chrome.tabs.onActivated.addListener(function() {
if (status == 'true'){
chrome.browserAction.setIcon({path: "icons/icon-active.png"});
}
else{
chrome.browserAction.setIcon({path: "icons/16x16.png"});
}
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.tabs.sendMessage(tabs[0].id, {status: status});
});
});
//send extension status on request
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
if (request.status == "getStatus")
sendResponse({status: status});
});
As you can see there are 3 functions:
change the status on browser action button click.
check status when you move to a different tab and notify content script (each tab has it's own "instance" of content script so disabling in one tab might still be active in another tab).
send response of the status on request from content script.
content script
// check extension status
chrome.runtime.sendMessage({status: "getStatus"}, function(response) {
if (response.status == 'true'){
// check elements mouse is hover
document.addEventListener("mouseover", setLink, true);
}
else{
document.removeEventListener("mouseover", setLink, true);
}
});
// wait for massage from background script
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
if (request.status == 'true'){
// check elements mouse is hover
document.addEventListener("mouseover", setLink, true);
}
else{
document.removeEventListener("mouseover", setLink, true);
}
});
Each content script should first check the status of the extension by sending massage to the background script and recieve a status update.
Also, if we turn the extension off in one tab, when we change tabs we will notify the content script of the change.
I'm sure that this can be done even better in terms of scripting but i hope it will help...

You should be achieve to do this using chrome.tabs.executeScript and chrome.tabs.insertCSS. Full example:
Background.js
chrome.browserAction.onClicked.addListener(function (tab) {
chrome.tabs.insertCSS(tab.id, {file: "content_style.css"});
chrome.tabs.executeScript(tab.id, {file: "content_script.js"});
});
Manifest.json
{
"name": "Inject js and CSS",
"version": "1",
"manifest_version": 2,
"browser_action": {
"default_icon": {
"16": "icon16.png",
"19": "icon19.png",
"32": "icon32.png",
"38": "icon38.png"
}
},
"background": {
"scripts": ["background.js"],
"persistent": false
},
"permissions": [
"activeTab"
]
}
Edit: Updated to use activeTab, event page and new icon sizes.

What do you think about that?
manifest.json
{
"manifest_version": 2,
"name": "Extension",
"description": "My extension",
"version": "0.1",
"icons": {
"16": "icons/icon_16.png",
"48": "icons/icon_48.png",
"128": "icons/icon_128.png"
},
"browser_action": {
"default_title": "Extension",
"default_icon": "icons/icon_16_disabled.png"
},
"background": {
"scripts": ["js/background.js"],
"persistent": true
},
"permissions": [
"activeTab",
"tabs"
]
}
background.js
var activedTab = {};
var injectedTab = {};
chrome.browserAction.onClicked.addListener(function(tab) {
if (typeof activedTab[tab.id] === 'undefined') {
activedTab[tab.id] = true;
chrome.tabs.insertCSS(tab.id, {file: 'style.css'});
chrome.tabs.executeScript(tab.id, {file: 'js/content.js'});
chrome.browserAction.setIcon({path: 'icons/icon_16.png'});
} else if (activedTab[tab.id]) {
activedTab[tab.id] = false;
chrome.browserAction.setIcon({path: 'icons/icon_16_disabled.png'});
if (injectedTab[tab.id]) {
chrome.tabs.sendMessage(tab.id, {greeting: 'hide'});
}
} else {
activedTab[tab.id] = true;
chrome.browserAction.setIcon({path: 'icons/icon_16.png'});
if (injectedTab[tab.id]) {
chrome.tabs.sendMessage(tab.id, {greeting: 'show'});
}
}
});
chrome.runtime.onMessage.addListener(function(request, sender) {
switch (request.greeting) {
case 'content_injected':
injectedTab[sender.tab.id] = true;
if (activedTab[sender.tab.id] == false) {
chrome.tabs.sendMessage(sender.tab.id, {greeting: 'hide'});
}
break;
}
});
chrome.tabs.onUpdated.addListener(function(tabId) {
delete activedTab[tabId];
chrome.browserAction.setIcon({path: 'icons/icon_16_disabled.png'});
});
chrome.tabs.onActiveChanged.addListener(function(tabId) {
if (activedTab[tabId]) {
chrome.browserAction.setIcon({path: 'icons/icon_16.png'});
} else {
chrome.browserAction.setIcon({path: 'icons/icon_16_disabled.png'});
}
});
content.js
console.log('loaded');
chrome.extension.sendMessage({greeting: 'content_injected'});
chrome.runtime.onMessage.addListener(function(request) {
switch (request.greeting) {
case 'show':
console.log('show');
break;
case 'hide':
console.log('hide');
break;
}
});

Related

I want to get the HTML of the web page, but I am getting the HTML of my chrome extension instead

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.

How to Access / Read any Text / Tag Value from the Webpage using Chrome Extension

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.

Chrome Message Passing API does not work

I'm trying to create an extension which tells me if a certain script is used on the current website I'm displaying in a tab.
I have a content script to see which scripts are used an to send messages to the my background script, which handles notification if a tab is switched and changes the icon of my extension to see if scripts are used or not.
This is my content script (load.js):
chrome.runtime.sendMessage({script_found: checkScript()});
function checkScript() {
var script_found = true;
var length = document.getElementsByTagName("script").length;
for (var i = 0; i < length; i++) {
var list = document.getElementsByTagName("script")[i].src;
var t = list.toString();
if (t.length > 1) {
if((t.indexOf("edge") != -1) || (t.indexOf("prototype") != -1)) {
script_found = false;
}
}
}
return script_found;
}
chrome.runtime.onMessage.addListener(
function(request, sendResponse) {
if(request.tabSwitch == true) {
sendResponse({script_found: checkScript()});
}
});
This is my background service (background.js):
chrome.tabs.onActivated.addListener(tabSwitch);
function tabSwitch() {
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.tabs.sendMessage(tabs[0].id, {tabSwitch: "true"},function(response) {
refreshIcon(response.script_found);
});
});
}
chrome.runtime.onMessage.addListener(
function(request) {
refreshIcon(request.script_found);
});
function refreshIcon(script_found) {
if(script_found) {
chrome.browserAction.setIcon({path:"good.png"})
} else if(!script_found) {
chrome.browserAction.setIcon({path:"error.png"})
}
}
I really do not know why it is not working on tabSwitch. It works fine when a website is loaded but it won't when I switch between tabs.
I hope you can help!
Thanks in advance!!
EDIT:
This is my manifest (manifest.json):
{
"manifest_version": 2,
"name": "Script Hunter",
"description": "This extension shows if certain scripts are used on this page.",
"version": "1.0",
"content_scripts": [{
"matches": ["<all_urls>"],
"js": ["load.js"],
"run_at": "document_idle"
}],
"background": {"scripts": ["background.js"]},
"browser_action": {
"default_icon": "good.png"
},
"permissions": [
"tabs",
"activeTab",
"https://ajax.googleapis.com/"
]
}
Thanks once again!
For background.js, you don't need to query active tab, you could just use the callback parameter of chrome.tabs.onActivated. (As #wOxxOm has mentioned in the comments, use tabSwitch: true here)
chrome.tabs.onActivated.addListener(tabSwitch);
function tabSwitch(activeInfo) {
chrome.tabs.sendMessage(activeInfo.tabId, {tabSwitch: true},function(response) {
refreshIcon(response.script_found);
});
}
In load.js, the third parameter for the callback of chrome.runtime.onMessage is sendResponse, however you only specify two parameters.
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
...
});

How to pass message from popup script to background script to content script

EDIT -- added background script
I need to get a message from the popup script in my chrome extension to the content script. It should send the message when a button inside the popup is clicked.
After reading more, it seems like you can't communicate directly between the popup script and the content script.
I think I need to go:
popup.js > background.js > script.js
I've tried doing this, but I can't seem to get it to work. There are a couple different ways to implement message passing, but not much documentation for this use case.
here's the code (it doesn't seem to pass a message at all right now):
popup.js
/* when something is saved in local storage... */
chrome.storage.sync.set({'myFilter': filter}, function(){
/* send message to background script */
chrome.runtime.sendMessage({greeting: "hello from popup"}, function(response) {
console.log(response.farewell);
});
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.tabs.sendMessage(tabs[0].id, {greeting: "new filter saved"}, function(response) {
console.log(response.farewell);
});
});
});
background.js
/*listen for message from popup, send message to content script */
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
console.log(sender.tab ?
"from a background script:" + sender.tab.url :
"from the extension");
if (request.greeting == "hello from popup") {
alert("message passed to background script");
console.log("message passed to background script");
/* send message to content script */
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.tabs.sendMessage(tabs[0].id, {greeting: "popup sent message"}, function(response) {
console.log(response.farewell);
});
});
return true;
sendResponse({farewell: "goodbye"});
}
return false;
});
script.js
/* get notice from background script*/
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
console.log(sender.tab ?
"from a content script:" + sender.tab.url :
"from the extension");
if (request.greeting == "popup sent message") {
alert("message passed to content script");
console.log("message passed to content script");
location.reload();
walkWithFilter();
return true;
sendResponse({farewell: "goodbye"});
}
return false;
});
manifest.json
{
"manifest_version": 2,
"name": "filter",
"description": "This extension allows twitter users to filter content in their feed",
"version": "1.0",
"content_scripts":
[
{
"matches": ["*://*/*"],
"js": ["bower_components/jquery/dist/jquery.min.js", "script.js"],
"run_at": "document_end"
}
],
"permissions": [
"tabs",
"storage",
"contextMenus",
"background",
"https://twitter.com/",
"http://twitter.com/"
],
"icons": {
"16": "fa-moon.png"
},
"background": {
"scripts": ["background.js"]
},
"browser_action": {
"default_title": "filter",
"default_icon": "fa-moon.png",
"default_popup": "popup.html"
}
}
right now this is doing nothing -- no alert message pops up and nothing is printed to the console when I pressed the button with the click event.
This is how it should run:
1) User enters input into popup window 'save-button' and clicks save
2) onclick of the save-button, input is saved in localstorage (this part works)
3) onclick of the save-button, message is sent from popup.js to script.js telling
it that new input has been saved to localstorage
4) Script.js receives message and prints to the regular console "message passed"
The reason I'm doing this is that I need to make the content script do some logic when it receives notice that new input has been saved in local storage. Does this seem reasonable?
You can actually go directly from
popup.js > script.js since the background page's messaging api is accessible to the popup too.
manifest.json
{
"manifest_version": 2,
"name": "filter",
"description": "This extension allows twitter users to filter content in their feed",
"version": "1.0",
"content_scripts":
[
{
"matches": ["<all_urls>"],
"js": ["script.js"],
"run_at": "document_end"
}
],
"permissions": [
"tabs",
"storage",
"contextMenus",
"background",
"https://twitter.com/",
"http://twitter.com/"
],
"icons": {
},
"background": {
"scripts": []
},
"browser_action": {
"default_title": "filter",
"default_popup": "popup.html"
}
}
popup.html
<button id="save-button">save-button</button>
<script src="/popup.js" type='text/javascript'></script>
popup.js
document.getElementById("save-button").onclick = function(){
console.log("clicked button");
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
if(tabs.length == 0){
console.log("could not send mesage to current tab");
}else{
chrome.tabs.sendMessage(tabs[0].id, {greeting: "hello, how are you content script?"}, function(response) {
console.log("received message from content script: "+response.farewell);
});
}
});
}
script.js
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
console.log(sender.tab ?
"from a content script:" + sender.tab.url :
"from the extension");
console.log("received message from popup: "+request.greeting);
sendResponse({farewell: "I'm good, thank you popup!"});
});
A very important permission for message passing i.e. tabs which is used to interact with the browser's tab system, is missing in your permission list.
Replace permissions array in manifest.json with
"permissions": [
"tabs",
"storage",
"contextMenus",
"background",
"https://twitter.com/",
"http://twitter.com/"
],

chrome extension - login in popup.html using django backend

I am trying to accomplish this login process.
In the extension, there is popup.html, popup.js, background.js and content.js as usual.
In popup.html, there are username and password inputfields and login button.
My vision is: I will catch both inputs in popup.js and send them to background.js and from there, i will send them to django backend. django backend returns logged-in token.
firstly, is this possible way?
my first step is not working: i cannot send message from popup.js to background.js. no error in console, also no success. this is my code:
popup.js
handleLoginClick_: function(){
var email = this.email.value;
var pwd = this.pwd.value;
chrome.tabs.sendMessage(tab.id, {asking: "login"}, function(response) {
sendToDjango(response.answer);
});
}
background.js
chrome.extension.onMessage.addListener(function(request, sender, sendResponse) {
if (request.asking === "login"){
console.log('got msg from popup.js');
return;
}
});
what am I doing wrong?
this is the manifest.json:
{
"name" : "myBIZMark",
"version" : "1.1",
"description" : "collect competing company's keywords",
"permissions": [
"tabs", "contextMenus", "<all_urls>", "storage"
],
"browser_action": {
"default_icon": "icon.png",
"default_popup": "popup.html"
},
"background": {
"persistent": false,
"scripts": ["jquery111.js","background.js"]
},
"manifest_version": 2,
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["jquery111.js", "contentscript.js","popup.js"]
}
]
}
Your message in popup.js is targeted toward the content scripts, not the background page. You need to use chrome.runtime, not chrome.tabs.
Replace:
chrome.tabs.sendMessage(tab.id, {asking: "login"}, function(response) {
sendToDjango(response.answer);
});
With:
chrome.runtime.sendMessage({asking: "login"}, function(response) {
sendToDjango(response.answer);
});
In summary:
chrome.tabs.sendMessage = background -> content
chrome.runtime.sendMessage = content -> background

Categories

Resources