Chrome Message Passing API does not work - javascript

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) {
...
});

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.

Chrome Extensions - How to run eval code on page load [duplicate]

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]);
}

Applying options to other scripts in Chrome extension

I'm writing my first Chrome extension and have hit a brick wall when it comes to setting and using "options." I've used Google's documentation to learn how to set up an options page and have opted to set it as my default popup in the extension.
Here's my manifest for reference:
{
"manifest_version": 2,
"name": "MyExtension",
"description": "MyDescription",
"version": "0.0",
"options_page": "options.html",
"browser_action": {
"default_icon": "on.png",
"default_popup": "options.html",
"default_title": "Manage Tools!"
},
"permissions": [
"storage",
"tabs",
"activeTab",
"https://ajax.googleapis.com/"
],
"content_scripts": [{
"matches": ["specialURL.com*"],
"js": ["jquery-3.1.1.min.js", "content.js"]
}],
"web_accessible_resources": [
"script.js"
],
"background": {
"scripts": ["background.js"]
}
}
My content.js page contains the following:
var s = document.createElement('script');
s.src = chrome.extension.getURL('script.js');
s.onload = function() {
this.remove();
};
(document.head || document.documentElement).appendChild(s);
which loads my script file (script.js). Inside of script.js there are two methods
function foo() { -code- }
function bar() { -code- }
options.js:
function save_options() {
var alltoggle = document.getElementById('alltoggle').checked;
var footoggle = document.getElementById('footoggle').checked;
var bartoggle = document.getElementById('bartoggle').checked;
chrome.storage.sync.set({
allsetting: alltoggle,
foosetting: footoggle,
barsetting: bartoggle
}, function () {
// Update status to let user know options were saved.
var status = document.getElementById('status');
status.textContent = 'Options saved.';
setTimeout(function () {
status.textContent = '';
}, 750);
});
}
// Restores select box and checkbox state using the preferences
// stored in chrome.storage.
function restore_options() {
// Default to true
chrome.storage.sync.get({
allsetting: true,
foosetting: true,
barsetting: true
}, function (items) {
document.getElementById('alltoggle').checked = items.allsetting;
document.getElementById('footoggle').checked = items.foosetting;
document.getElementById('bartoggle').checked = items.barsetting;
});
}
document.addEventListener('DOMContentLoaded', restore_options);
document.getElementById('save').addEventListener('click', save_options);
document.addEventListener('DOMContentLoaded', function () {
document.querySelector('#alltoggle').addEventListener('change', allHandler);
// Turn on/off all features
function allHandler() {
$("input:checkbox").prop('checked', $(this).prop("checked"));
}
});
The problem comes in when I try to load settings back and apply them to my content scripts. Specifically, I can't find how to do that anywhere.
Solved
At first I thought this was a messages issue, but it wasn't. By adding the following to content.js I was able to check storage for my settings and execute code from there.
var fooON;
chrome.storage.sync.get("foosetting", function(result) {
fooON = result.foosetting;
//confirm
console.log(result.foosetting);
});
var barON;
chrome.storage.sync.get("barsetting", function(result) {
barON = result.barsetting;
//confirm
console.log(result.barsetting);
});
Then, by separating foo() and bar() into two scripts I could make the script injection in content.js selective by adding if(fooOn){-inject foo()-} etc.
For others facing similar issues
You can access options saved to storage.sync by using the chrome.storage.sync.get() API call in your content scripts.
var yourSetting;
chrome.storage.sync.get("yourSetting", function(result) {
yourSetting = result.yourSetting;
});

Chrome extension: Add content (enable/disable icon)

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;
}
});

Message Passing is not working in chrome extention

I am try to messaging passing from my Default_popup.js to content script and when it's receive at content script end then try to save data on chrome storage. but my code is not working properly.
This code was worked 2-3 times. but now it's not working.
manifest.json
{
"manifest_version": 2,
"name": "Test",
"description": "Automated Test Tool.",
"version": "1.0",
"browser_action": {
"default_icon": "icon.png",
"default_popup": "Default_Popup.html"
},
"content_scripts": [{
"matches":["http://*/*","https://*/*"],
"js":["myscript.js"] }
],
"background":{
"scripts":["background.js"]
},
"permissions":[
"storage",
"notifications",
"tabs",
"http://*/",
"https://*/"
]
}
Popup.js
chrome.tabs.getSelected(null, function (tab) {
var Jour = {};
Jour.FromStation = $('#txtFromStation').val();
Jour.ToStation = $('#txtToStation').val();
Jour.JourneyDate = $('#datepicker').val();
chrome.tabs.sendRequest(tab.id, { JourneyDetails: Jour }, function handler(response) {
alert("Inside Client = " + "Done");
});
});
myscript.js //Content Script
window.onload = function () {
chrome.extension.onRequest.addListener(
function (request, sender, sendResponse) {
alert('request.JourneyDetails.FromStation');
alert(request.JourneyDetails.FromStation);
var Jour = {};
Jour.FromStation = request.FromStation;
Jour.ToStation = request.ToStation;
Jour.JourneyDate = request.JourneyDate;
chrome.storage.sync.set({ JourneyDetails: Jour }, function () {
console.log('Setting Saved')
});
//sendResponse({ counter2: "5" });
}
);
}
chrome.tabs.getSelected() is deprecated, try using chrome.tabs.query() instead? https://developer.chrome.com/extensions/tabs#method-getSelected
chrome.tabs.sendRequest() and chrome.extension.onRequest() are deprecated, try using chrome.tabs.sendMessage() and chrome.runtime.onMessage() instead? https://developer.chrome.com/extensions/tabs#method-sendRequest, https://developer.chrome.com/extensions/extension#event-onRequest
Do you include jQuery in Default_Popup.html? What are the errors you are seeing?
Are the elements with ids $('#txtFromStation'), $('#txtToStation'), $('#datepicker') in Default_Popup.html? If you could post the content of Default_Popup.html that will be helpful.

Categories

Resources